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 增加了对以下功能的支持:
借助这些功能,可以强类型化许多转换函数及其参数列表的高阶函数。
带有元组类型的剩余参数
当剩余参数具有元组类型时,该元组类型会展开为一系列离散参数。 例如,以下两个声明是等价的:
declare function foo(...args: [number, string, boolean]): void;declare function foo(args_0: number, args_1: string, args_2: boolean): void;带有元组类型的展开表达式
当函数调用的最后一个参数是元组类型的展开表达式时,该展开表达式对应于元组元素类型的一系列离散参数。
因此,以下调用是等价的:
const args: [number, string, boolean] = [42, "hello", true];
foo(42, "hello", true);
foo(args[0], args[1], args[2]);
foo(...args);泛型剩余参数
剩余参数可以具有受数组类型约束的泛型类型,并且类型推断可以为这种泛型剩余参数推断出元组类型。这使得高阶捕获和展开部分参数列表成为可能:
示例
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 声明中,类型推断分别为 T、U 和 V 推断出类型 number、[string, boolean] 和 void。
注意,当从一系列参数推断出元组类型并随后展开为参数列表时(如 U 的情况),原始参数名称会在展开中使用(但是,名称没有语义含义,在其他地方也不可观察)。
元组类型中的可选元素
元组类型现在允许在元素类型后添加 ? 后缀,以指示该元素是可选的:
示例
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 元素。
示例
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 引入了一个新的顶级类型 unknown。 unknown 是 any 的类型安全对应物。任何类型都可以赋值给 unknown,但 unknown 只能赋值给自身和 any(没有类型断言或基于控制流的收窄)。同样,在没有首先断言或收窄到更具体的类型之前,不允许对 unknown 进行任何操作。
示例
// 在交集中,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 的特定行为。
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 默认初始化器:
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 运行时构造函数如 Symbol 或 Iterable)的声明文件作者,建议使用三斜杠引用 lib 指令。以前,这些 .d.ts 文件不得不添加此类类型的前向/重复声明。
示例
在编译中的一个文件中使用 /// <reference lib="es2017.string" /> 等同于使用 --lib es2017.string 进行编译。
/// <reference lib="es2017.string" />
"foo".padStart(4);