วิธีดึง Type ใน @line/liff ด้วย Typescript

Suchit Pongnumkul
incubate.co.th
Published in
2 min readAug 14, 2020

--

LINE ออก npm ของ LIFF มาแล้ว มี type มาให้ด้วย เย้ แต่… ไม่ยอม export type ต่างๆออกมาให้ใช้กัน แล้วเราจะเอา type ตรงนั้นออกมายังไงดี โดยที่ไม่ต้องไปก๊อป code มาแปะใหม่

สำหรับคนที่ใช้ Javascript โดยปกติก็คงไม่ได้กังวลกับ type ของตัวแปรอยู่แล้ว แต่ถ้าคนที่ใช้ Typescript โดยเฉพาะ Angular แล้วล่ะก็ คงไม่อยากเห็น “any” หรือ “object” อยู่ใน code ของเรากันแน่ๆ และเมื่อ LINE ออก npm สำหรับ LIFF มา ตอนนั้น LINE Developers ก็น่าจะดีใจกันมาก (ผู้เขียนก็เป็นหนึ่งในคนที่ดีใจสุดๆเช่นกัน) แต่พอเอามาเล่นจริงๆแล้ว ดันเจอว่า ใน npm มีการกำหนด type ไว้ แต่ไม่ export ออกมาให้!!! ครั้นจะให้ไปก๊อป code ที่อยู่ใน package อยู่แล้วมาแปะใหม่หรือเขียนขึ้นเองก็ใช่ที่ เลยต้องไปหาวิธีดึง type ต่างๆออกมา เพื่อเอามาสร้างตัวแปรรับค่าที่ได้จากการเรียกใช้งาน function ต่างๆใน package @line/liff

ตัวอย่าง type ที่อยู่ใน package:

File: ./node_modules/@line/liff/dist/lib/common/getOS.d.ts

declare type OS = ‘ios’ | ‘android’ | ‘web’ | undefined;
/**
* Gets the environment in which the user is running the LIFF app.
* @export
*/
export default function getOS(): OS;
/**
* Cleanup cached OS, testing use
* @export
*/
export declare function _cleanupCachedOS(): void;
export {};

จะเห็นได้ว่า type OS ไม่ได้ถูก export ออกมา ทำให้ไม่สามารถเรียกใช้ type OS ได้โดยตรง ซึ่งเวลาจะเอาค่าที่ได้จาก function getOS ไปใช้ ก็รู้แค่ว่าเป็น string หรือ undefined เท่านั้น แต่ไม่มีการตรวจสอบตอน compile ว่าต้องเป็นคำว่า ‘ios’ หรือ ‘android’ หรือ ‘web’ เท่านั้น

เพื่อให้เราได้ type OS ออกมา โดยไม่ต้องก๊อป code เราสามารถใช้ ReturnType<Type> ซึ่งเป็นหนึ่งใน Utility Types ของ Typescript

ซึ่งสามารถเรียกใช้ได้ด้วย:

os: ReturnType<typeof liff.getOS>;

โดย typeof เป็นตัวช่วยแปลง ตัวแปร กลับเป็น type ซึ่ง liff.getOS เป็นชนิด function ที่คืนค่าออกมาเป็น type OS เมื่อเราใส่ typeof liff.getOS ลงในส่วน generic ของ ReturnType ทำให้ได้ type OS ออกมาใช้ได้

แล้วถ้าเป็น type ที่อยู่ใน onFulfilled ของ Promise ล่ะ ทำไงดี….

ลองมาดู file ./node_modules/@line/liff/dist/lib/api/getProfile.d.ts กัน

interface Profile {
userId: string;
displayName: string;
pictureUrl?: string;
statusMessage?: string;
}
/**
* Gets the current user’s profile.
* @export
* @returns {Promise<Profile>}
*/
export default function getProfile(): Promise<Profile>;
export {};

จะเห็นว่า getProfile.d.ts ทำการ export แต่ function getProfile ที่คืนค่า Promise แต่ไม่ได้ export interface Profile มาด้วย ซึ่งถ้าเราจะใช้ ก็อาจจะต้องก๊อปมาแปะ ใน code กันเองใหม่ หรืออาจจะใช้ keyword infer ของ Typescript ช่วย

โดยกำหนด generic type:

type UnPromise<T> = T extends Promise<infer X>? X : T;

แล้วเรียกใช้ โดย:

profile: UnPromise<ReturnType<typeof liff.getProfile>>;

ซึ่งตัว keyword infer นี้จะใช้ร่วมกับ keyword extends ของ conditional types

เมื่อเอาทั้งสองตัวอย่างมารวมกัน ก็จะได้ code ดังนี้

และสามารถไปเปิดเล่นได้ที่ LIFF ในไลน์กันได้เลย

แถม: ถ้าต้องการ type ของ argument ตัวที่ n ใน function f สามารถ ใช้ Utility Type ที่ชื่อ Parameters<Type> ได้ ด้วยคำสั่ง: Parameters< typeof f >[n]

--

--

Suchit Pongnumkul
incubate.co.th

LINE Certified Coach 2019 for API who studied Management of Technology and do development and consulting