1. unknown

unknown 指的是不可预先定义的类型,在很多场景下,它可以代替 any 的功能同时保留静态类型检查的能力,而 any 是放弃了静态检查

1
2
3
4
const a: unknown = 123;
a.split(''); // Error 静态检查不通过
const b: any = 234;
b.split('') // Pass 没有静态检查,运行时才会报错

unknown 的一个使用场景是,避免使用 any 作为函数的参数类型而导致的静态类型检查 bug:

1
2
3
4
5
6
function test(input: unknown): number {
if (Array.isArray(input)) {
return input.length; // Pass: 这个代码块中,类型守卫已经将input识别为array类型
}
return input.length; // Error: 这里的input还是unknown类型,静态检查报错。但是如果入参类型设置为any,那就会放弃检查,带来运行报错风险
}

2. void

在 TS 中,void 和 undefined 功能高度相似,可以在逻辑上避免不小心使用空指针导致的报错。

1
2
function f() {} // 空函数没有任何返回值,返回类型推测为void
const a = f(); // 此时a的类型为void, 不能调用a的任何属性和方法

void 和 undefined类型最大的区别是,void并不在意返回值的类型,返不返回 返回什么值都无所谓,undefined则需要返回undefined类型,可以理解为undefined是void的一个子集。

1
2
3
4
5
6
7
8
9
type Fn = () => void;  // 这里的void表示逻辑上不关注具体的返回值类型,number、string、undefined等都可以
let f: Fn = function(): string {
return '345'
};

type Fn = () => undefined;
let f: Fn = function() {
return '345'; // Error 只能返回undefined
};

3. never

never 是指没法正常结束返回的类型,一个必定会报错或者死循环的函数会返回这样的类型。

1
2
3
function foo(): never { throw new Error('error message') }  // throw error 返回值是never
function foo2(): never { while(true){} } // 这个死循环的也会无法正常退出
function foo3(): never { let count = 1; while(count){ count++; } } // Error: 这个无法将返回值定义为never,因为无法在静态编译阶段直接识别出

还有就是永远没有相交的交叉差类型:

1
type human = 'boy' & 'girl' // 这两个单独的字符串类型并不可能相交,故human为never类型

不过任何类型联合上 never 类型,还是原来的类型:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
type language = 'ts' | never   // language的类型还是'ts'类型

可以通过这个特性做一些使用的功能: 过滤出Module属性为函数的属性名
interface Module {
count: number;
message: string;
asyncMethod: () => string;
syncMethod: () => number;
}

type FuncName<T> = {
[K in keyof T]: T[K] extends Function ? K : never; // 是函数的话把键名赋值给值,不是函数则值设置为never类型
}[keyof T]; // 拿出Module的所有值的集合:never | never | 'asyncMethod' | 'syncMethod',所以最终为'asyncMethod' | 'syncMethod'

type Result = FuncName<Module>; // 'asyncMethod' | 'syncMethod'

never 还有以下两个特性

  • 无法把其他类型赋给 never,any也不行
    1
    2
    3
    let n: never;
    let o: any = {};
    n = o; // Error: 不能把一个非never类型赋值给never类型,包括any
  • 在一个函数中调用了返回 never 的函数后,之后的代码都会变成 deadcode
    1
    2
    3
    4
    5
    6
    7
    8
    function foo(): never {
    while (true) {}
    }

    function test() {
    foo(); // 这里的foo指上面返回never的函数
    console.log(111); // Error: 编译器报错,此行代码永远不会执行到
    }

4. const断言

1
2
3
4
它的语法是一个类型断言,用 const 代替类型名称(例如 123 as const)断言构造新的文字表达式时,我们可以向语言发出以下信号:
· 该表达式中的字面类型不应被扩展(例如:不能从“hello”转换为字符串)
· 对象文字获取readonly属性
· 数组文字变成readonly元组
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// 写法
let x = "hello" as const;
let x = <const>"hello"

// 不用const断言类型会被扩展
let x = 'x'; // typeof x: string
let x = 'x' as const; // typeof x: 'x'

let obj = {
x: 10,
y: [20, 30],
z: {
a: { b: 42 }
}
};
// 不用const
let obj: {
x: number,
y: number[],
z: {
a: { b: number }
}
};
// 用const断言
let obj: {
readonly x: 10;
readonly y: readonly [20, 30];
readonly z: {
readonly a: {
readonly b: 42;
};
};

注意事项

  1. const断言只能立即应用于简单的文字表达式。

    1
    2
    3
    4
    5
    6
    // Error! A 'const' assertion can only be applied to a
    // to a string, number, boolean, array, or object literal.
    let a = (Math.random() < 0.5 ? 0 : 1) as const;

    // Works!
    let b = Math.random() < 0.5 ? (0 as const) : (1 as const);
  2. 要记住的另一件事是const上下文不会立即将表达式转换为完全不可变的

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    let arr = [1, 2, 3, 4];

    let foo = {
    name: "foo",
    contents: arr
    } as const;

    foo.name = "bar"; // error!
    foo.contents = []; // error!

    foo.contents.push(5); // ...works!

5. 可变参数元祖类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// 函数concat采用两个数组或元组类型并将它们连接在一起以创建一个新数组。
// 旧版本
function concat<A2>(arr1: [], arr2: [A2]): [A2];
function concat<A1, A2>(arr1: [A1], arr2: [A2]): [A1, A2];
function concat<A1, B1, A2>(arr1: [A1, B1], arr2: [A2]): [A1, B1, A2];
function concat<A1, B1, C1, A2>(arr1: [A1, B1, C1], arr2: [A2]): [A1, B1, C1, A2];
function concat<A1, B1, C1, D1, A2>(arr1: [A1, B1, C1, D1], arr2: [A2]): [A1, B1, C1, D1, A2];
function concat<A1, B1, C1, D1, E1, A2>(arr1: [A1, B1, C1, D1, E1], arr2: [A2]): [A1, B1, C1, D1, E1, A2];
function concat<A1, B1, C1, D1, E1, F1, A2>(arr1: [A1, B1, C1, D1, E1, F1], arr2: [A2]): [A1, B1, C1, D1, E1, F1, A2];
function concat(arr1, arr2) {
return [...arr1, ...arr2];
}

// 新特性
type Strings = [string, string];
type Numbers = [number, number];

type StrStrNumNumBool = [...Strings, ...Numbers, boolean];

// 实现
type Arr = readonly any[];

function concat<T extends Arr, U extends Arr>(arr1: T, arr2: U): [...T, ...U] {
return [...arr1, ...arr2];
}

let arr1 = [1, '2', true] as const;
let arr2 = ['2', undefined] as const;
let r = concat(arr1, arr2); // [1, '2', true, '2', undefined]

该功能本身很棒,但在更复杂的场景中也很有用。例如,例如实现一个函数 partialCall,接受一个函数 f、以及f期望的最初几个参数,调用后,返回一个新的函数,该函数接受f需要的剩余参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
type Arr = readonly any[];

function partialCall<T extends Arr, U extends Arr, R>(
f: (...args: [...T, ...U]) => R,
...headArgs: T
) {
return (...tailArgs: U) => f(...headArgs, ...tailArgs);
}

const foo = (x: string, y: number, z: boolean) => {};

const f1 = partialCall(foo, 100);
// error: Argument of type 'number' is not assignable to parameter of type 'string'.

const f2 = partialCall(foo, "hello", 100, true, "oops");
// error: Expected 4 arguments, but got 5.

const f3 = partialCall(foo, "hello"); // Works! => const f3:(y: number, z: boolean) => void
f3(123, true); // Works!
f3(123, "hello");
// error: Argument of type 'string' is not assignable to parameter of type 'boolean'.
f3();
// error: Expected 2 arguments, but got 0.

6. infer

infer 一种类型分发, 表示在 extends 条件语句中推断类型的变量;相当于是一个占位符,给一个 未知的类型 进行推断分发。

简单的例子(获取函数参数)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
type ParamType<T> = T extends (param: infer P) => any ? P : T;

在这个条件语句 T extends (param: infer P) => any ? P : T 中,infer P 表示待推断的函数参数。

整句表示为:如果 T 能赋值给 (param: infer P) => any,则结果是 (param: infer P) => any 类型中的参数 P,否则返回为 T。

interface User {
name: string;
age: number;
}

function f(user: User) {
return user.name;
}

type Param = ParamType<typeof f>; // Param = User
type AA = ParamType<string>; // string

type ReturnType<T> = T extends (...args: any[]) => infer R ? R : any;

一些用例

  • tupleunion ,如:[string, number] -> string | number

    1
    2
    3
    4
    5
    6
    7
    8
    9
    type ElementOf<T> = T extends Array<infer E> ? E : never

    type TTuple = [string, number];

    type ToUnion = ElementOf<TTuple>; // string | number

    方法2:
    type TTuple = [string, number];
    type Res = TTuple[number]; // string | number
  • unionintersection,如:string | number -> string & number

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    分布条件类型
    例如:T extends U ? X : Y,当 T 是 A|B 时候,会拆分为A extends U ? X : Y | B 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 T10 = TypeName<string | (() => void)>; // "string" | "function"
    type T12 = TypeName<string | string[] | undefined>; // "string" | "object" | "undefined"
    type T11 = TypeName<string[] | number[]>; // "object"


    同一类型变量的多个候选类型将会被推断为交叉类型
    type Bar<T> = T extends { a: (x: infer U) => void, b: (x: infer U) => void } ? U : never;
    type T20 = Bar<{ a: (x: string) => void, b: (x: string) => void }>; // string
    type T21 = Bar<{ a: (x: string) => void, b: (x: number) => void }>; // string & number

    type UnionToIntersection<U> =
    (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never;

    type Result = UnionToIntersection<{a: string} | {b:number}>; // {a: string} & {b: number}
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    interface Action<T> {
    payload?: T
    type: string
    }

    interface Module {
    count: number;
    message: string;
    asyncMethod<T, U>(input: Promise<T>): Promise<Action<U>>;
    syncMethod<T, U>(action: Action<T>): Action<U>;
    }

    type Result {
    asyncMethod<T, U>(input: T): Action<U>;
    syncMethod<T, U>(action: T): Action<U>;
    }

    将Module转换为Result;

    type FuncName<T> = {
    [K in keyof T]: T[K] extends Function ? K : never;
    }[keyof T];

    type TransformF<P> = {
    [K in keyof P]: P[K] extends <T, U>(arg: Promise<T>) => Promise<Action<U>>
    ? <T, U>(arg: T) => Action<U>
    : P[K] extends <T, U>(arg: Action<T>) => Action<U>
    ? <T, U>(arg: Action<T>) => Action<U>
    : never;
    }
    type Result = TransformF<Pick<Module, FuncName<Module>>>;

最后更新: 2021年02月09日 14:24

原始链接: https://xmtd.github.io/2021/01/12/typescript/

× 请我吃糖~
打赏二维码