Typescript类型声明

Typescript类型声明

基础类型

  1. boolean
  2. number
  3. string
  4. symbol
  5. 空值null
  6. 未定义undefined
  7. 数组T[]Array<T>
  8. 元组Tuple[T, K]
  9. 枚举enum
  10. 非原始类型object,即除number,string,boolean,symbol,null或undefined之外的类型
  11. 任何类型any
  12. 没有任何类型void
  13. 永不存在值的类型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"

keyofT[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
}

类型约束通常和类型索引一起使用,例如我们有一个方法专门用来获取对象的值,但是这个对象并不确定,我们就可以使用 extendskeyof 进行约束。

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中剔除nullundefined
  • 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 提供了许多内置的类型工具(如 PartialRequired 等),合理利用这些工具可以简化类型定义和转换。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值