Skip to content
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待

TypeScript 3.0

项目引用

TypeScript 3.0 引入了项目引用的新概念。项目引用允许 TypeScript 项目依赖于其他 TypeScript 项目——具体来说,允许 tsconfig.json 文件引用其他 tsconfig.json 文件。指定这些依赖关系使得将代码拆分成更小的项目变得更容易,因为它为 TypeScript(及其周围的工具)提供了一种理解构建顺序和输出结构的方法。

TypeScript 3.0 还为 tsc 引入了一种新模式,即 --build 标志,它与项目引用协同工作,以实现更快的 TypeScript 构建。

更多文档请参阅项目引用手册页面

元组在剩余参数和展开表达式中的支持

TypeScript 3.0 增加了与函数参数列表作为元组类型交互的多项新功能。 TypeScript 3.0 增加了对以下功能的支持:

借助这些功能,可以强类型化许多转换函数及其参数列表的高阶函数。

带有元组类型的剩余参数

当剩余参数具有元组类型时,该元组类型会展开为一系列离散参数。 例如,以下两个声明是等价的:

ts
declare function foo(...args: [number, string, boolean]): void;
ts
declare function foo(args_0: number, args_1: string, args_2: boolean): void;

带有元组类型的展开表达式

当函数调用的最后一个参数是元组类型的展开表达式时,该展开表达式对应于元组元素类型的一系列离散参数。

因此,以下调用是等价的:

ts
const args: [number, string, boolean] = [42, "hello", true];
foo(42, "hello", true);
foo(args[0], args[1], args[2]);
foo(...args);

泛型剩余参数

剩余参数可以具有受数组类型约束的泛型类型,并且类型推断可以为这种泛型剩余参数推断出元组类型。这使得高阶捕获和展开部分参数列表成为可能:

示例
ts
declare function bind<T, U extends any[], V>(
  f: (x: T, ...args: U) => V,
  x: T
): (...args: U) => V;

declare function f3(x: number, y: string, z: boolean): void;

const f2 = bind(f3, 42); // (y: string, z: boolean) => void
const f1 = bind(f2, "hello"); // (z: boolean) => void
const f0 = bind(f1, true); // () => void

f3(42, "hello", true);
f2("hello", true);
f1(true);
f0();

在上面的 f2 声明中,类型推断分别为 TUV 推断出类型 number[string, boolean]void

注意,当从一系列参数推断出元组类型并随后展开为参数列表时(如 U 的情况),原始参数名称会在展开中使用(但是,名称没有语义含义,在其他地方也不可观察)。

元组类型中的可选元素

元组类型现在允许在元素类型后添加 ? 后缀,以指示该元素是可选的:

示例
ts
let t: [number, string?, boolean?];
t = [42, "hello", true];
t = [42, "hello"];
t = [42];

strictNullChecks 模式下,? 修饰符会自动将 undefined 包含在元素类型中,类似于可选参数。

如果元组类型的元素类型带有 ? 后缀,并且其右侧的所有元素也带有 ? 修饰符,则允许省略该元素。

当为剩余参数推断元组类型时,源中的可选参数会成为推断类型中的可选元组元素。

带有可选元素的元组类型的 length 属性是表示可能长度的数字字面量类型的联合。例如,元组类型 [number, string?, boolean?]length 属性的类型是 1 | 2 | 3

元组类型中的剩余元素

元组类型的最后一个元素可以是 ...X 形式的剩余元素,其中 X 是一个数组类型。剩余元素表示元组类型是开放式的,可以有零个或多个额外的数组元素类型的元素。例如,[number, ...string[]] 表示一个元组,其中第一个元素是 number,后跟任意数量的 string 元素。

示例
ts
function tuple<T extends any[]>(...args: T): T {
  return args;
}

const numbers: number[] = getArrayOfNumbers();
const t1 = tuple("foo", 1, true); // [string, number, boolean]
const t2 = tuple("bar", ...numbers); // [string, ...number[]]

带有剩余元素的元组类型的 length 属性的类型是 number

新的 unknown 顶级类型

TypeScript 3.0 引入了一个新的顶级类型 unknownunknownany 的类型安全对应物。任何类型都可以赋值给 unknown,但 unknown 只能赋值给自身和 any(没有类型断言或基于控制流的收窄)。同样,在没有首先断言或收窄到更具体的类型之前,不允许对 unknown 进行任何操作。

示例
ts
// 在交集中,unknown 会被吸收

type T00 = unknown & null; // null
type T01 = unknown & undefined; // undefined
type T02 = unknown & null & undefined; // null & undefined (变为 never)
type T03 = unknown & string; // string
type T04 = unknown & string[]; // string[]
type T05 = unknown & unknown; // unknown
type T06 = unknown & any; // any

// 在联合中,unknown 吸收一切

type T10 = unknown | null; // unknown
type T11 = unknown | undefined; // unknown
type T12 = unknown | null | undefined; // unknown
type T13 = unknown | string; // unknown
type T14 = unknown | string[]; // unknown
type T15 = unknown | unknown; // unknown
type T16 = unknown | any; // any

// 类型变量与 unknown 在联合和交集中

type T20<T> = T & {}; // T & {}
type T21<T> = T | {}; // T | {}
type T22<T> = T & unknown; // T
type T23<T> = T | unknown; // unknown

// 条件类型中的 unknown

type T30<T> = unknown extends T ? true : false; // 延迟
type T31<T> = T extends unknown ? true : false; // 延迟(所以会分发)
type T32<T> = never extends T ? true : false; // true
type T33<T> = T extends never ? true : false; // 延迟

// keyof unknown

type T40 = keyof any; // string | number | symbol
type T41 = keyof unknown; // never

// 只允许对 unknown 使用相等运算符

function f10(x: unknown) {
  x == 5;
  x !== 10;
  x >= 0; // 错误
  x + 1; // 错误
  x * 2; // 错误
  -x; // 错误
  +x; // 错误
}

// 不允许属性访问、元素访问或函数调用

function f11(x: unknown) {
  x.foo; // 错误
  x[5]; // 错误
  x(); // 错误
  new x(); // 错误
}

// typeof、instanceof 和用户定义的类型谓词

declare function isFunction(x: unknown): x is Function;

function f20(x: unknown) {
  if (typeof x === "string" || typeof x === "number") {
    x; // string | number
  }
  if (x instanceof Error) {
    x; // Error
  }
  if (isFunction(x)) {
    x; // Function
  }
}

// 对 unknown 的同态映射类型

type T50<T> = { [P in keyof T]: number };
type T51 = T50<any>; // { [x: string]: number }
type T52 = T50<unknown>; // {}

// 任何类型都可以赋值给 unknown

function f21<T>(pAny: any, pNever: never, pT: T) {
  let x: unknown;
  x = 123;
  x = "hello";
  x = [1, 2, 3];
  x = new Error();
  x = x;
  x = pAny;
  x = pNever;
  x = pT;
}

// unknown 只能赋值给自身和 any

function f22(x: unknown) {
  let v1: any = x;
  let v2: unknown = x;
  let v3: object = x; // 错误
  let v4: string = x; // 错误
  let v5: string[] = x; // 错误
  let v6: {} = x; // 错误
  let v7: {} | null | undefined = x; // 错误
}

// 类型参数 'T extends unknown' 与 object 无关

function f23<T extends unknown>(x: T) {
  let y: object = x; // 错误
}

// 除原始类型外,任何类型都可赋值给 { [x: string]: unknown }

function f24(x: { [x: string]: unknown }) {
  x = {};
  x = { a: 5 };
  x = [1, 2, 3];
  x = 123; // 错误
}

// 类型为 unknown 的局部变量总是被视为已初始化

function f25() {
  let x: unknown;
  let y = x;
}

// 展开 unknown 会导致结果类型为 unknown

function f26(x: {}, y: unknown, z: any) {
  let o1 = { a: 42, ...x }; // { a: number }
  let o2 = { a: 42, ...x, ...y }; // unknown
  let o3 = { a: 42, ...x, ...y, ...z }; // any
}

// 返回类型为 unknown 的函数不需要返回语句

function f27(): unknown {}

// 无法从 unknown 创建剩余类型

function f28(x: unknown) {
  let { ...a } = x; // 错误
}

// 类型为 unknown 的类属性不需要明确赋值

class C1 {
  a: string; // 错误
  b: unknown;
  c: any;
}

JSX 中对 defaultProps 的支持

TypeScript 2.9 及更早版本未在 JSX 组件中利用 React defaultProps 声明。用户通常必须将属性声明为可选,并在 render 中使用非空断言,或者在导出组件之前使用类型断言来修复组件的类型。

TypeScript 3.0 在 JSX 命名空间中添加了一个新的类型别名 LibraryManagedAttributes。此辅助类型定义了在用于检查针对它的 JSX 表达式之前,对组件的 Props 类型进行的转换;从而允许自定义,例如:如何处理提供的 props 和推断的 props 之间的冲突、如何映射推断、如何处理可选性,以及如何组合来自不同位置的推断。

简而言之,使用这个通用类型,我们可以为诸如 defaultProps 以及某种程度上的 propTypes 等事物建模 React 的特定行为。

tsx
export interface Props {
  name: string;
}

export class Greet extends React.Component<Props> {
  render() {
    const { name } = this.props;
    return <div>Hello {name.toUpperCase()}!</div>;
  }
  static defaultProps = { name: "world" };
}

// 类型检查通过!无需类型断言!
let el = <Greet />;

注意事项

defaultProps 上的显式类型

默认属性是从 defaultProps 属性类型推断出来的。如果添加了显式类型注释,例如 static defaultProps: Partial<Props>;,编译器将无法识别哪些属性具有默认值(因为 defaultProps 的类型包含 Props 的所有属性)。

使用 static defaultProps: Pick<Props, "name">; 作为显式类型注释,或者像上面的示例一样不添加类型注释。

对于函数组件(以前称为 SFC),使用 ES2015 默认初始化器:

tsx
function Greet({ name = "world" }: Props) {
  return <div>Hello {name.toUpperCase()}!</div>;
}

@types/React 的更改

仍然需要对 @types/React 中的 JSX 命名空间添加 LibraryManagedAttributes 定义的相关更改。请注意,存在一些限制。

/// <reference lib="..." /> 引用指令

TypeScript 添加了一个新的三斜杠引用指令(/// <reference lib="name" />),允许文件显式包含现有的内置 lib 文件。

内置 lib 文件的引用方式与 tsconfig.json 中的 lib 编译器选项相同(例如使用 lib="es2015" 而不是 lib="lib.es2015.d.ts" 等)。

对于依赖内置类型(如 DOM API 或内置 JS 运行时构造函数如 SymbolIterable)的声明文件作者,建议使用三斜杠引用 lib 指令。以前,这些 .d.ts 文件不得不添加此类类型的前向/重复声明。

示例

在编译中的一个文件中使用 /// <reference lib="es2017.string" /> 等同于使用 --lib es2017.string 进行编译。

ts
/// <reference lib="es2017.string" />

"foo".padStart(4);