Typescript类型声明
基础类型
booleannumberstringsymbol- 空值
null - 未定义
undefined - 数组
T[]或Array<T> - 元组Tuple
[T, K] - 枚举
enum - 非原始类型
object,即除number,string,boolean,symbol,null或undefined之外的类型 - 任何类型
any - 没有任何类型
void - 永不存在值的类型
never(如抛出异常)
类型断言
类型断言有两种形式。 其一是“尖括号”语法:
let someValue: any = "this is a string";
let strLength: number = (<string>someValue).length;`
另一个为as语法:
let someValue: any = "this is a string";
let strLength: number = (someValue as string).length;`
两种形式是等价的。
注意:当你在TypeScript里使用JSX时,只有 as语法断言是被允许的。
使用 常量断言as const 可以让 TypeScript 推断出更具体的类型。
let tuple = [1, "two"] as const;
// TypeScript 推断 tuple 的类型为 readonly [1, "two"]
高级类型
交叉类型(Intersection Types):T&U
通过 & 将多个类型合并为一个类型,包含了所需的所有类型的特性,本质上是一种并的操作。
适用于接口合并场景:
interface Person {
name: string;
}
interface Leader {
job: string;
}
// 把接口Person和接口Leade合并为一个新类型PersonLeader,这个类型同时拥有两个接口的特性
type PersonLeader = Person & Leader;
const pb: PersonLeader = {
name: 'tom',
job: 'boss'
};
适用于对象合并场景,如下将声明一个函数,将两个对象合并成一个对象并返回:
function extend<T, U>(first: T, second: U): T & U {
const result = <T & U>{};
for (let key in first) {
(<any>result)[key] = (<any>first)[key];
}
for (let key in second) {
if (!result.hasOwnProperty(key)) {
(<any>result)[key] = (<any>second)[key];
}
}
return result;
}
function extend<T, U>(first: T, second: U): T & U {
let result: T & U = { ...first, ...second };
return result;
}
联合类型(Union Types):T | U
联合类型的语法规则和逻辑 “或” 的符号一致,表示其类型为连接的多个类型中的任意一个,本质上是一个交的关系。
function formatCommandLine(command: string[] | string): string {
return typeof command === 'string' ? command.trim() : command.join(' ').trim();
}
类型保护(Type Guards) :类型保护是一种特定的函数,其作用是确定一个联合类型变量是哪种类型,返回值是一个 类型谓词。
function isString(value: number | string): value is string {
return typeof value === "string";
}
value is string就是类型谓词。 谓词为 parameterName is Type这种形式, parameterName必须是来自于当前函数签名里的一个参数名。
字面量类型
字符串字面量类型允许你指定字符串必须的固定值。如type Color = "red" | "blue";
数字字面量类型:type Values = 1 | 2 | 3 | 4;
模板字面量类型允许你使用字符串字面量来创建新的类型。我们可以将多个字符串类型组合成一个新的字符串类型。例如:
type Color = "red" | "blue";
type Size = "small" | "large";
type ColoredSize = `${Color}-${Size}`;
类型别名(Type Aliases):type <alias> = <typeName>
类型别名会给一个类型起个新名字,类型别名有时和接口很像,但是可以作用于原始值、联合类型、元组以及其它任何你需要手写的类型。
可以使用 type SomeName = someValidTypeAnnotation的语法来创建类型别名:
type some = boolean | string
const b: some = true // ok
const c: some = 'hello' // ok
const d: some = 123 // 不能将类型“123”分配给类型“some”
此外类型别名可以是泛型:
type Container<T> = { value: T };
也可以使用类型别名在属性里引用自己:
type Tree<T> = {
value: T;
left: Tree<T>;
right: Tree<T>;
}
可以看到,类型别名和接口使用十分相似,都可以描述一个对象或者函数
两者最大的区别在于,interface只能用于定义对象类型,而 type 的声明方式除了对象之外还可以定义交叉、联合、原始类型等,类型声明的方式适用范围显然更加广泛。
索引类型(Index types)
使用索引类型,编译器就能够检查使用了动态属性名的代码。
索引签名类型 使用场景:当无法确定对象中有哪些属性(或者说对象中可以出现任意多个属性)
interfact AnyObject {
[key: string]: number;
}
- key 只是一个占位符,可改成任意合法变量名
- JS 中对象的键为 string 类型
索引查询类型 又称,索引访问类型,用于查询属性类型。
type Props = { a: number; b: number; c: number };
type TypeA = Props['a'];
- TypeA 为Props 中 a 的类型,即number。
[]中出现的属性必须存在于被查询类型中,否则会报错。
索引类型查询操作符 keyof 类似于 Object.keys ,用于获取一个接口中 Key 的联合类型。
interface Button {
type: string
text: string
}
type ButtonKeys = keyof Button
// 等效于
type ButtonKeys = "type" | "text"
keyof 和 T[K]与字符串索引签名进行交互。 如果你有一个带有字符串索引签名的类型,那么 keyof T会是 string, 并且 T[string]为索引签名的类型:
interface Map<T> {
[key: string]: T;
}
let keys: keyof Map<number>; // string
let value: Map<number>['foo']; // number
通过关键字 extends 进行 类型约束,不同于在 class 后使用 extends 的继承作用,泛型内使用的主要作用是对泛型加以约束。
type BaseType = string | number | boolean
// 这里表示 copy 的参数
// 只能是字符串、数字、布尔这几种基础类型
function copy<T extends BaseType>(arg: T): T {
return arg
}
类型约束通常和类型索引一起使用,例如我们有一个方法专门用来获取对象的值,但是这个对象并不确定,我们就可以使用 extends 和 keyof 进行约束。
function getValue<T, K extends keyof T>(obj: T, key: K) {
return obj[key]
}
const obj = { a: 1 }
const a = getValue(obj, 'a')
映射类型(Mapped Types):in
通过 in 关键字做类型的映射,遍历已有接口的 key 或者是遍历联合类型,如下例子:
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
interface Obj {
a: string
b: string
}
type ReadOnlyObj = Readonly<Obj>
上述的结构,可以分成这些步骤:
-
keyof T:通过类型索引keyof的得到联合类型'a' | 'b' -
P in keyof T等同于p in 'a' | 'b',相当于执行了一次forEach的逻辑,遍历'a' | 'b'
所以最终ReadOnlyObj的接口为下述:
interface ReadOnlyObj {
readonly a: string;
readonly b: string;
}
条件类型(Conditional Types)
条件类型的语法规则和三元表达式一致,经常用于一些类型不确定的情况。
T extends U ? X : Y
上面的意思就是,如果 T 是 U 的子集,就是类型 X,否则为类型 Y。
infer 关键字主要用于在条件类型中推断类型变量。
基本语法如下:type MyType<T> = T extends SomeType<infer U> ? U : OtherType;
这里的 infer U 表示:如果 T 能够匹配 SomeType<...> 这种结构,那么就把尖括号里的类型推断出来,赋值给 U,并在 ? 后面使用它。
1. 提取数组元素类型
type ElementType<T> = T extends Array<infer U> ? U : never;
type A = ElementType<number[]>; // number
type B = ElementType<string[]>; // string
type C = ElementType<boolean>; // never
解释:
- 如果
T是数组类型(如number[]),则推断出元素类型U。 - 如果不是数组类型,则返回
never。
2. 提取函数返回值类型
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : any;
type Fn = (a: number, b: string) => boolean;
type Result = ReturnType<Fn>; // boolean
解释:
- 如果
T是函数类型,则推断其返回值类型R。 - 否则返回
any。
3. 提取函数参数类型
type Parameters<T> = T extends (...args: infer P) => any ? P : never;
type Fn = (a: number, b: string) => boolean;
type Params = Parameters<Fn>; // [number, string]
解释:
- 如果
T是函数类型,则推断其参数类型组成的元组P。
4. 提取 Promise 的结果类型
type UnpackedPromise<T> = T extends Promise<infer U> ? U : T;
type A = UnpackedPromise<Promise<number>>; // number
type B = UnpackedPromise<string>; // string
5. 提取对象属性类型
type PropertyType<T, K extends keyof T> = T extends { [P in K]: infer U } ? U : never;
type Obj = { name: string; age: number };
type NameType = PropertyType<Obj, 'name'>; // string
6. 递归提取嵌套 Promise 的最终类型
type DeepUnpacked<T> = T extends Promise<infer U> ? DeepUnpacked<U> : T;
type A = DeepUnpacked<Promise<Promise<Promise<number>>>>; // number
7. 提取类的实例类型
class Person {
name: string;
constructor(name: string) {
this.name = name;
}
}
type InstanceType<T> = T extends new (...args: any[]) => infer R ? R : any;
type P = InstanceType<typeof Person>; // Person
8. 提取联合类型中的某一部分
type ExtractArrayType<T> = T extends (infer U)[] ? U : never;
type A = ExtractArrayType<string[] | number[]>; // string | number
8. 提取函数的 this 类型
type ThisParameterType<T> = T extends (this: infer U, ...args: any[]) => any ? U : unknown;
function fn(this: Date, x: number) {}
type T = ThisParameterType<typeof fn>; // Date
9. 提取元组的最后一个元素
type Last<T extends any[]> = T extends [...infer Rest, infer Last] ? Last : never;
type A = Last<[1, 2, 3]>; // 3
10. 提取字符串字面量类型的前缀
type Prefix<T extends string> = T extends `${infer P}_${string}` ? P : never;
type A = Prefix<'hello_world'>; // 'hello'
type B = Prefix<'foo_bar_baz'>; // 'foo'
11. 提取对象所有属性的类型组成的联合类型
type ValueOf<T> = T extends { [key: string]: infer V } ? V : never;
type Obj = { a: number; b: string; c: boolean };
type V = ValueOf<Obj>; // number | string | boolean
类型工具
Partial<Type>:将Type的所有属性设为可选。Required<Type>:将Type的所有属性设为必填。Readonly<Type>:将Type的所有属性设为只读。Pick<Type,Keys>:从Type中选取Keys指定的属性组成新类型。Pick<T, Exclude<keyof T, K>>等同于Omit<T,K>Record<Keys,Type>:创建一个对象类型,键为Keys,值为Type。Exclude<T, U>:从T中剔除可以赋值给U的类型。Extract<T, U>:提取T中可以赋值给U的类型。NonNullable<T>:从T中剔除null和undefined。ReturnType<T>:获取函数返回值类型。InstanceType<T>:获取构造函数类型的实例类型。
type T01 = Extract<"a" | "b" | "c" | "d", "a" | "c" | "f">; // "a" | "c"
type T02 = Exclude<string | number | (() => void), Function>; // string | number
type T03 = Extract<string | number | (() => void), Function>; // () => void
type T04 = NonNullable<(() => string) | string[] | null | undefined>; // (() => string) | string[]
function f1(s: string) {
return { a: 1, b: s };
}
class C {
x = 0;
y = 0;
}
type T10 = ReturnType<() => string>; // string
type T11 = ReturnType<(s: string) => void>; // void
type T12 = ReturnType<(<T>() => T)>; // {}
type T13 = ReturnType<(<T extends U, U extends number[]>() => T)>; // number[]
type T14 = ReturnType<typeof f1>; // { a: number, b: string }
type T15 = ReturnType<any>; // any
type T16 = ReturnType<never>; // any
type T17 = ReturnType<string>; // Error
type T18 = ReturnType<Function>; // Error
type T20 = InstanceType<typeof C>; // C
type T21 = InstanceType<any>; // any
type T22 = InstanceType<never>; // any
type T23 = InstanceType<string>; // Error
type T24 = InstanceType<Function>; // Error
总结
使用 TypeScript 高级类型的好处
- 提高代码可读性:通过明确的类型定义,开发者可以更容易地理解代码的意图和数据结构。
- 增强代码健壮性:类型系统可以在编译阶段捕获潜在的错误,减少运行时错误的发生。
- 支持复杂的数据结构:高级类型允许开发者以更灵活、更精确的方式描述复杂的数据结构。
使用 TypeScript 高级类型时需要注意的事项
- 避免过度复杂化:虽然高级类型提供了强大的功能,但过度使用可能会导致代码变得难以理解和维护。
- 注意类型兼容性:在使用联合类型、交叉类型等高级类型时,需要仔细考虑类型之间的兼容性,以避免类型错误。
- 合理利用类型工具:TypeScript 提供了许多内置的类型工具(如
Partial、Required等),合理利用这些工具可以简化类型定义和转换。

3488

被折叠的 条评论
为什么被折叠?



