TypeScript 3.0

项目引用

¥Project References

TypeScript 3.0 引入了项目引用的新概念。项目引用允许 TypeScript 项目依赖于其他 TypeScript 项目。 - 具体来说,允许 tsconfig.json 文件引用其他 tsconfig.json 文件。指定这些依赖可以更轻松地将代码拆分为更小的项目,因为它为 TypeScript(及其相关工具)提供了一种理解构建顺序和输出结构的方法。

¥TypeScript 3.0 introduces a new concept of project references. Project references allow TypeScript projects to depend on other TypeScript projects - specifically, allowing tsconfig.json files to reference other tsconfig.json files. Specifying these dependencies makes it easier to split your code into smaller projects, since it gives TypeScript (and tools around it) a way to understand build ordering and output structure.

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

¥TypeScript 3.0 also introduces a new mode for tsc, the --build flag, that works hand-in-hand with project references to enable faster TypeScript builds.

有关更多文档,请参阅 项目参考手册页面

¥See Project References handbook page for more documentation.

剩余参数和扩展表达式中的元组

¥Tuples in rest parameters and spread expressions

TypeScript 3.0 增加了对多项新功能的支持,可以将函数参数列表作为元组类型进行交互。TypeScript 3.0 增加了对以下内容的支持:

¥TypeScript 3.0 adds support to multiple new capabilities to interact with function parameter lists as tuple types. TypeScript 3.0 adds support for:

借助这些特性,我们可以将一些高阶函数强类型化,从而转换函数及其参数列表。

¥With these features it becomes possible to strongly type a number of higher-order functions that transform functions and their parameter lists.

元组类型的剩余参数

¥Rest parameters with tuple types

当剩余参数具有元组类型时,元组类型将扩展为离散参数序列。例如,以下两个声明是等效的:

¥When a rest parameter has a tuple type, the tuple type is expanded into a sequence of discrete parameters. For example the following two declarations are equivalent:

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

元组类型的展开表达式

¥Spread expressions with tuple types

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

¥When a function call includes a spread expression of a tuple type as the last argument, the spread expression corresponds to a sequence of discrete arguments of the tuple element types.

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

¥Thus, the following calls are equivalent:

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

泛型剩余参数

¥Generic rest parameters

剩余参数允许具有受限于数组类型的泛型,并且类型推断可以推断此类泛型剩余参数的元组类型。这支持高阶捕获和扩展部分参数列表:

¥A rest parameter is permitted to have a generic type that is constrained to an array type, and type inference can infer tuple types for such generic rest parameters. This enables higher-order capturing and spreading of partial parameter lists:

示例

¥Example

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 类型。

¥In the declaration of f2 above, type inference infers types number, [string, boolean] and void for T, U and V respectively.

请注意,当从参数序列推断出元组类型,然后将其扩展为参数列表时(例如 U 的情况),扩展中使用原始参数名称(但是,这些名称没有语义含义,并且无法以其他方式观察到)。

¥Note that when a tuple type is inferred from a sequence of parameters and later expanded into a parameter list, as is the case for U, the original parameter names are used in the expansion (however, the names have no semantic meaning and are not otherwise observable).

元组类型中的可选元素

¥Optional elements in tuple types

元组类型现在允许在元素类型上使用 ? 后缀来指示该元素是可选的:

¥Tuple types now permit a ? postfix on element types to indicate that the element is optional:

示例

¥Example

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

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

¥In strictNullChecks mode, a ? modifier automatically includes undefined in the element type, similar to optional parameters.

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

¥A tuple type permits an element to be omitted if it has a postfix ? modifier on its type and all elements to the right of it also have ? modifiers.

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

¥When tuple types are inferred for rest parameters, optional parameters in the source become optional tuple elements in the inferred type.

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

¥The length property of a tuple type with optional elements is a union of numeric literal types representing the possible lengths. For example, the type of the length property in the tuple type [number, string?, boolean?] is 1 | 2 | 3.

元组类型中的剩余元素

¥Rest elements in tuple types

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

¥The last element of a tuple type can be a rest element of the form ...X, where X is an array type. A rest element indicates that the tuple type is open-ended and may have zero or more additional elements of the array element type. For example, [number, ...string[]] means tuples with a number element followed by any number of string elements.

示例

¥Example

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

¥The type of the length property of a tuple type with a rest element is number.

新的 unknown 顶层类型

¥New unknown top type

TypeScript 3.0 引入了一个新的顶层类型 unknownunknownany 的类型安全对应物。任何值都可以赋值给 unknown,但 unknown 只能赋值给其自身和 any,除非进行类型断言或基于控制流的收缩。同样,如果没有先断言或缩小到更具体的类型,则不允许对 unknown 进行任何操作。

¥TypeScript 3.0 introduces a new top type unknown. unknown is the type-safe counterpart of any. Anything is assignable to unknown, but unknown isn’t assignable to anything but itself and any without a type assertion or a control flow based narrowing. Likewise, no operations are permitted on an unknown without first asserting or narrowing to a more specific type.

示例

¥Example

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

¥Support for defaultProps in JSX

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

¥TypeScript 2.9 and earlier didn’t leverage React defaultProps declarations inside JSX components. Users would often have to declare properties optional and use non-null assertions inside of render, or they’d use type-assertions to fix up the type of the component before exporting it.

TypeScript 3.0 在 JSX 命名空间中增加了对名为 LibraryManagedAttributes 的新类型别名的支持。此辅助类型定义了组件 Props 类型的转换,然后再用于检查针对该类型的 JSX 表达式;因此允许进行如下自定义:如何处理提供的 props 和推断的 props 之间的冲突,如何映射推断,如何处理可选性,以及如何组合来自不同位置的推断。

¥TypeScript 3.0 adds support for a new type alias in the JSX namespace called LibraryManagedAttributes. This helper type defines a transformation on the component’s Props type, before using to check a JSX expression targeting it; thus allowing customization like: how conflicts between provided props and inferred props are handled, how inferences are mapped, how optionality is handled, and how inferences from differing places should be combined.

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

¥In short using this general type, we can model React’s specific behavior for things like defaultProps and, to some extent, 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 />;

警告

¥Caveats

defaultProps 上的显式类型

¥Explicit types on defaultProps

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

¥The default-ed properties are inferred from the defaultProps property type. If an explicit type annotation is added, e.g. static defaultProps: Partial<Props>; the compiler will not be able to identify which properties have defaults (since the type of defaultProps include all properties of Props).

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

¥Use static defaultProps: Pick<Props, "name">; as an explicit type annotation instead, or do not add a type annotation as done in the example above.

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

¥For function components (formerly known as SFCs) use ES2015 default initializers:

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

@types/React 的变更

¥Changes to @types/React

仍然需要进行相应的更改,将 LibraryManagedAttributes 定义添加到 @types/React 中的 JSX 命名空间。请记住,存在一些限制。

¥Corresponding changes to add LibraryManagedAttributes definition to the JSX namespace in @types/React are still needed. Keep in mind that there are some limitations.

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

¥/// <reference lib="..." /> reference directives

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

¥TypeScript adds a new triple-slash-reference directive (/// <reference lib="name" />), allowing a file to explicitly include an existing built-in lib file.

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

¥Built-in lib files are referenced in the same fashion as the lib compiler option in tsconfig.json (e.g. use lib="es2015" and not lib="lib.es2015.d.ts", etc.).

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

¥For declaration file authors who rely on built-in types, e.g. DOM APIs or built-in JS run-time constructors like Symbol or Iterable, triple-slash-reference lib directives are recommended. Previously these .d.ts files had to add forward/duplicate declarations of such types.

示例

¥Example

在编译过程中对某个文件使用 /// <reference lib="es2017.string" /> 相当于使用 --lib es2017.string 进行编译。

¥Using /// <reference lib="es2017.string" /> to one of the files in a compilation is equivalent to compiling with --lib es2017.string.

ts
/// <reference lib="es2017.string" />
"foo".padStart(4);