文章来源于网络收集而来,版权归原创者所有,如有侵权请及时联系!
十五、高级类型
15.1 交叉类型(取并集)
交叉类型是将多个类型合并为一个类型。这让我们可以把现有的多种类型叠加到一起成为一种类型,它包含了所需的所有类型的特性。 例如, Person & Loggable
同时是 Person
和 Loggable
。就是说这个类型的对象同时拥有了这两种类型的成员。
我们大多是在混入(mixins)或其它不适合典型面向对象模型的地方看到交叉类型的使用。 (在 JavaScript 里发生这种情况的场合很多!)下面是如何创建混入的一个简单例子
function extend<T, U> (first: T, second: U): T & U { let result = {} as T & U for (let id in first) { result[id] = first[id] as any } for (let id in second) { if (!result.hasOwnProperty(id)) { result[id] = second[id] as any } } return result } class Person { constructor (public name: string) { } } interface Loggable { log (): void } class ConsoleLogger implements Loggable { log () { // ... } } var jim = extend(new Person('Jim'), new ConsoleLogger()) var n = jim.name jim.log()
interface DogInterface { run(): void } interface CatInterface { jump(): void } // pet 具备两个接口的所有方法 let pet: DogInterface & CatInterface = { run() {}, jump() {} } // 联合类型 let a: number | string = 1 let b: 'a' | 'b' | 'c' // 字面量联合类型 let c: 1 | 2 | 3 // 数字联合类型 class Dog implements DogInterface { run() {} eat() {} } class Cat implements CatInterface { jump() {} eat() {} } enum Master { Boy, Girl } function getPet(master: Master) { let pet = master === Master.Boy ? new Dog() : new Cat(); // pet.run() // pet.jump() pet.eat() return pet } interface Square { kind: "square"; size: number; } interface Rectangle { kind: "rectangle"; width: number; height: number; } interface Circle { kind: "circle"; radius: number; } type Shape = Square | Rectangle | Circle function area(s: Shape) { switch (s.kind) { case "square": return s.size * s.size; case "rectangle": return s.height * s.width; case 'circle': return Math.PI * s.radius ** 2 default: return ((e: never) => {throw new Error(e)})(s) } } console.log(area({kind: 'circle', radius: 1}))
15.2 索引类型
let obj = { a: 1, b: 2, c: 3 } // function getValues(obj: any, keys: string[]) { // return keys.map(key => obj[key]) // } function getValues<T, K extends keyof T>(obj: T, keys: K[]): T[K][] { return keys.map(key => obj[key]) } console.log(getValues(obj, ['a', 'b'])) // console.log(getValues(obj, ['d', 'e'])) // keyof T interface Obj { a: number; b: string; } let key: keyof Obj // T[K] let value: Obj['a'] // T extends U
15.3 映射类型
interface Obj { a: string; b: number; } // 使得每个成员属性变为只读 type ReadonlyObj = Readonly<Obj> // 把一个接口属性变为可选 type PartialObj = Partial<Obj> // 抽取 obj 的子集 type PickObj = Pick<Obj, 'a' | 'b'> type RecordObj = Record<'x' | 'y', Obj>
15.4 条件类型
// T extends U ? X : Y type TypeName<T> = T extends string ? "string" : T extends number ? "number" : T extends boolean ? "boolean" : T extends undefined ? "undefined" : T extends Function ? "function" : "object"; type T1 = TypeName<string> type T2 = TypeName<string[]> // (A | B) extends U ? X : Y // (A extends U ? X : Y) | (B extends U ? X : Y) type T3 = TypeName<string | string[]> type Diff<T, U> = T extends U ? never : T type T4 = Diff<"a" | "b" | "c", "a" | "e"> // Diff<"a", "a" | "e"> | Diff<"b", "a" | "e"> | Diff<"c", "a" | "e"> // never | "b" | "c" // "b" | "c" type NotNull<T> = Diff<T, null | undefined> type T5 = NotNull<string | number | undefined | null> // Exclude<T, U> // NonNullable<T> // Extract<T, U> type T6 = Extract<"a" | "b" | "c", "a" | "e"> // ReturnType<T> type T8 = ReturnType<() => string>
15.5 联合类型
联合类型与交叉类型很有关联,但是使用上却完全不同。 偶尔你会遇到这种情况,一个代码库希望传入 number
或 string
类型的参数。 例如下面的函数
function padLeft(value: string, padding: any) { if (typeof padding === 'number') { return Array(padding + 1).join(' ') + value } if (typeof padding === 'string') { return padding + value } throw new Error(`Expected string or number, got '${padding}'.`) } padLeft('Hello world', 4) // returns " Hello world"
padLeft 存在一个问题,padding 参数的类型指定成了 any。 这就是说我们可以传入一个既不是 number 也不是 string 类型的参数,但是 TypeScript 却不报错
let indentedString = padLeft('Hello world', true) // 编译阶段通过,运行时报错
为了解决这个问题,我们可以使用 联合类型做为 padding
的参数
function padLeft(value: string, padding: string | number) { // ... } let indentedString = padLeft('Hello world', true) // 编译阶段报错
- 联合类型表示一个值可以是几种类型之一。我们用竖线(
|
)分隔每个类型,所以number | string
表示一个值可以是number
或string
。
如果一个值是联合类型, 我们只能访问此联合类型的所有类型里共有的成员
interface Bird { fly() layEggs() } interface Fish { swim() layEggs() } function getSmallPet(): Fish | Bird { // ... } let pet = getSmallPet() pet.layEggs() // okay pet.swim() // error
这里的联合类型可能有点复杂:如果一个值的类型是 A | B
,我们能够确定的是它包含了 A
和 B
中共有的成员。这个例子里,Fish 具有一个 swim
方法,我们不能确定一个 Bird | Fish
类型的变量是否有 swim
方法。 如果变量在运行时是 Bird 类型,那么调用 pet.swim()
就出错了
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论