TypeScript 3.0

项目引用 (Project References)

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
// In an intersection everything absorbs unknown
type T00 = unknown & null; // null
type T01 = unknown & undefined; // undefined
type T02 = unknown & null & undefined; // null & undefined (which becomes never)
type T03 = unknown & string; // string
type T04 = unknown & string[]; // string[]
type T05 = unknown & unknown; // unknown
type T06 = unknown & any; // any
// In a union an unknown absorbs everything
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
// Type variable and unknown in union and intersection
type T20<T> = T & {}; // T & {}
type T21<T> = T | {}; // T | {}
type T22<T> = T & unknown; // T
type T23<T> = T | unknown; // unknown
// unknown in conditional types
type T30<T> = unknown extends T ? true : false; // Deferred
type T31<T> = T extends unknown ? true : false; // Deferred (so it distributes)
type T32<T> = never extends T ? true : false; // true
type T33<T> = T extends never ? true : false; // Deferred
// keyof unknown
type T40 = keyof any; // string | number | symbol
type T41 = keyof unknown; // never
// Only equality operators are allowed with unknown
function f10(x: unknown) {
x == 5;
x !== 10;
x >= 0; // Error
x + 1; // Error
x * 2; // Error
-x; // Error
+x; // Error
}
// No property accesses, element accesses, or function calls
function f11(x: unknown) {
x.foo; // Error
x[5]; // Error
x(); // Error
new x(); // Error
}
// typeof, instanceof, and user defined type predicates
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
}
}
// Homomorphic mapped type over unknown
type T50<T> = { [P in keyof T]: number };
type T51 = T50<any>; // { [x: string]: number }
type T52 = T50<unknown>; // {}
// Anything is assignable to 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 assignable only to itself and any
function f22(x: unknown) {
let v1: any = x;
let v2: unknown = x;
let v3: object = x; // Error
let v4: string = x; // Error
let v5: string[] = x; // Error
let v6: {} = x; // Error
let v7: {} | null | undefined = x; // Error
}
// Type parameter 'T extends unknown' not related to object
function f23<T extends unknown>(x: T) {
let y: object = x; // Error
}
// Anything but primitive assignable to { [x: string]: unknown }
function f24(x: { [x: string]: unknown }) {
x = {};
x = { a: 5 };
x = [1, 2, 3];
x = 123; // Error
}
// Locals of type unknown always considered initialized
function f25() {
let x: unknown;
let y = x;
}
// Spread of unknown causes result to be 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
}
// Functions with unknown return type don't need return expressions
function f27(): unknown {}
// Rest type cannot be created from unknown
function f28(x: unknown) {
let { ...a } = x; // Error
}
// Class properties of type unknown don't need definite assignment
class C1 {
a: string; // Error
b: unknown;
c: any;
}

支持 JSX 中的 defaultProps

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

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

简而言之,使用此通用类型,我们可以模拟 React 在 defaultProps 以及某种程度上的 propTypes 等方面的特定行为。

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" };
}
// Type-checks! No type assertions needed!
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 中进行相应的更改,以将 LibraryManagedAttributes 定义添加到 JSX 命名空间中。请记住,这存在一些局限性。

/// <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);

TypeScript 文档是一个开源项目。请提交 Pull Request 来帮助我们改进这些页面 ❤

此页面的贡献者
MHMohamed Hegazy (53)
OTOrta Therox (13)
RCRyan Cavanaugh (2)
RIRuslan Iusupov (2)
EIEugene Ilyin (1)
11+

最后更新:2026 年 3 月 27 日