如下問題來自於與公司小夥伴以及網友的討論,整理成章,但願提供另外一種思路(避免踩坑)解決問題。
TypeScript 提供函數重載的功能,用來處理因函數參數不一樣而返回類型不一樣的使用場景,使用時,只需爲同一個函數定義多個類型便可,簡單使用以下所示:html
declare function test(a: number): number; declare function test(a: string): string; const resS = test('Hello World'); // resS 被推斷出類型爲 string; const resN = test(1234); // resN 被推斷出類型爲 number;
它也適用於參數不一樣,返回值類型相同的場景,咱們只須要知道在哪一種函數類型定義下能使用哪些參數便可。git
考慮以下例子:github
interface User { name: string; age: number; } declare function test(para: User | number, flag?: boolean): number;
在這個 test
函數裏,咱們的本意多是當傳入參數 para
是 User
時,不傳 flag
,當傳入 para
是 number
時,傳入 flag
。TypeScript 並不知道這些,當你傳入 para
爲 User
時,flag
一樣容許你傳入:typescript
const user = { name: 'Jack', age: 666 } // 沒有報錯,可是與想法違背 const res = test(user, false);
使用函數重載能幫助咱們實現:後端
interface User { name: string; age: number; } declare function test(para: User): number; declare function test(para: number, flag: boolean): number; const user = { name: 'Jack', age: 666 }; // bingo // Error: 參數不匹配 const res = test(user, false);
實際項目中,你可能要多寫幾步,如在 class
中:數組
interface User { name: string; age: number; } const user = { name: 'Jack', age: 123 }; class SomeClass { /** * 註釋 1 */ public test(para: User): number; /** * 註釋 2 */ public test(para: number, flag: boolean): number; public test(para: User | number, flag?: boolean): number { // 具體實現 return 11; } } const someClass = new SomeClass(); // ok someClass.test(user); someClass.test(123, false); // Error someClass.test(123); someClass.test(user, false);
自從 TypeScript 2.1 版本推出映射類型以來,它便不斷被完善與加強。在 2.1 版本中,能夠經過 keyof
拿到對象 key
類型, 內置 Partial
、Readonly
、Record
、Pick
映射類型;2.3 版本增長 ThisType
;2.8 版本增長 Exclude
、Extract
、NonNullable
、ReturnType
、InstanceType
;同時在此版本中增長條件類型與加強 keyof
的能力;3.1 版本支持對元組與數組的映射。這些無不意味着映射類型在 TypeScript 有着舉足輕重的地位。app
其中 ThisType
並無出如今官方文檔中,它主要用來在對象字面量中鍵入 this
:編輯器
// Compile with --noImplicitThis type ObjectDescriptor<D, M> = { data?: D; methods?: M & ThisType<D & M>; // Type of 'this' in methods is D & M } function makeObject<D, M>(desc: ObjectDescriptor<D, M>): D & M { let data: object = desc.data || {}; let methods: object = desc.methods || {}; return { ...data, ...methods } as D & M; } let obj = makeObject({ data: { x: 0, y: 0 }, methods: { moveBy(dx: number, dy: number) { this.x += dx; // Strongly typed this this.y += dy; // Strongly typed this } } }); obj.x = 10; obj.y = 20; obj.moveBy(5, 5);
正是因爲
ThisType
的出現,Vue 2.5 才得以加強對 TypeScript 的支持。
雖已內置了不少映射類型,但在不少時候,咱們須要根據本身的項目自定義映射類型:函數
好比你可能想取出接口類型中的函數類型:this
type FunctionPropertyNames<T> = { [K in keyof T]: T[K] extends Function ? K : never }[keyof T]; type FunctionProperties<T> = Pick<T, FunctionPropertyNames<T>>; interface Part { id: number; name: string; subparts: Part[]; updatePart(newName: string): void; } type T40 = FunctionPropertyNames<Part>; // "updatePart" type T42 = FunctionProperties<Part>; // { updatePart(newName: string): void }
好比你可能爲了便捷,把本屬於某個屬性下的方法,經過一些方式 alias 到其餘地方。
舉個例子:SomeClass
下有個屬性 value = [1, 2, 3]
,你可能在 Decorators 給類添加了此種功能:在 SomeClass
裏調用 this.find()
時,其實是調用 this.value.find()
,可是此時 TypeScript 並不知道這些:
class SomeClass { value = [1, 2, 3]; someMethod() { this.value.find(/* ... */); // ok this.find(/* ... */); // Error:SomeClass 沒有 find 方法。 } }
藉助於映射類型,和 interface + class
的聲明方式,能夠實現咱們的目的:
type ArrayMethodName = 'filter' | 'forEach' | 'find'; type SelectArrayMethod<T> = { [K in ArrayMethodName]: Array<T>[K] } interface SomeClass extends SelectArrayMethod<number> {} class SomeClass { value = [1, 2, 3]; someMethod() { this.forEach(/* ... */) // ok this.find(/* ... */) // ok this.filter(/* ... */) // ok this.value // ok this.someMethod() // ok } } const someClass = new SomeClass(); someClass.forEach(/* ... */) // ok someClass.find(/* ... */) // ok someClass.filter(/* ... */) // ok someClass.value // ok someClass.someMethod() // ok
導出
SomeClass
類時,也能使用。
可能有點不足的地方,在這段代碼裏 interface SomeClass extends SelectArrayMethod<number> {}
你須要手動添加範型的具體類型(暫時沒想到更好方式)。
類型斷言用來明確的告訴 TypeScript 值的詳細類型,合理使用能減小咱們的工做量。
好比一個變量並無初始值,可是咱們知道它的類型信息(它多是從後端返回)有什麼辦法既能正確推導類型信息,又能正常運行了?有一種網上的推薦方式是設置初始值,而後使用 typeof
拿到類型(可能會給其餘地方用)。然而我可能比較懶,不喜歡設置初始值,這時候使用類型斷言能夠解決這類問題:
interface User { name: string; age: number; } export default class NewRoom extends Vue { private user = {} as User; }
在設置初始化時,添加斷言,咱們就無須添加初始值,編輯器也能正常的給予代碼提示了。若是 user
屬性不少,這樣就能解決大量沒必要要的工做了,定義的 interface
也能給其餘地方使用。
枚舉類型分爲數字類型與字符串類型,其中數字類型的枚舉能夠當標誌使用:
// https://github.com/Microsoft/TypeScript/blob/master/src/compiler/types.ts#L3859 export const enum ObjectFlags { Class = 1 << 0, // Class Interface = 1 << 1, // Interface Reference = 1 << 2, // Generic type reference Tuple = 1 << 3, // Synthesized generic tuple type Anonymous = 1 << 4, // Anonymous Mapped = 1 << 5, // Mapped Instantiated = 1 << 6, // Instantiated anonymous or mapped type ObjectLiteral = 1 << 7, // Originates in an object literal EvolvingArray = 1 << 8, // Evolving array type ObjectLiteralPatternWithComputedProperties = 1 << 9, // Object literal pattern with computed properties ContainsSpread = 1 << 10, // Object literal contains spread operation ReverseMapped = 1 << 11, // Object contains a property from a reverse-mapped type JsxAttributes = 1 << 12, // Jsx attributes type MarkerType = 1 << 13, // Marker type used for variance probing JSLiteral = 1 << 14, // Object type declared in JS - disables errors on read/write of nonexisting members ClassOrInterface = Class | Interface }
在 TypeScript src/compiler/types
源碼裏,定義了大量如上所示的基於數字類型的常量枚舉。它們是一種有效存儲和表示布爾值集合的方法。
在 《深刻理解 TypeScript》 中有一個使用例子:
enum AnimalFlags { None = 0, HasClaws = 1 << 0, CanFly = 1 << 1, HasClawsOrCanFly = HasClaws | CanFly } interface Animal { flags: AnimalFlags; [key: string]: any; } function printAnimalAbilities(animal: Animal) { var animalFlags = animal.flags; if (animalFlags & AnimalFlags.HasClaws) { console.log('animal has claws'); } if (animalFlags & AnimalFlags.CanFly) { console.log('animal can fly'); } if (animalFlags == AnimalFlags.None) { console.log('nothing'); } } var animal = { flags: AnimalFlags.None }; printAnimalAbilities(animal); // nothing animal.flags |= AnimalFlags.HasClaws; printAnimalAbilities(animal); // animal has claws animal.flags &= ~AnimalFlags.HasClaws; printAnimalAbilities(animal); // nothing animal.flags |= AnimalFlags.HasClaws | AnimalFlags.CanFly; printAnimalAbilities(animal); // animal has claws, animal can fly
上例代碼中 |=
用來添加一個標誌,&=
和 ~
用來刪除標誌,|
用來合併標誌。