最近更新 VSCode 的时候,提示其内置的 Typescript 也到了 3.5.1 的版本,所以看了一下 TypeScript 3.5 的更新文档,进行了简单的翻译,具体内容以下:git
typescript 3.4 版本为了修复一个 bug 致使了类型检查变慢,构建时间大大增长、使用编辑器时有卡顿感。github
TypeScript 3.5 作了一些优化,目前在许多增量检查中实际上会比 TypeScript 3.3 更快。typescript
--incremental
构建TypeScript 3.4 引入了一个新的 --incremental
编译器选项。此选项将大量信息保存到.tsbuildinfo
文件中,该文件可用于加速后续对 tsc
的调用。api
TypeScript 3.5 进行了一些优化,目前在几百个项目的 --build
场景中,与TypeScript 3.4相比,重建的时间能够减小68% !!浏览器
Omit
终于内置lib.d.ts
如今内置 Omit
了,以下安全
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
复制代码
所以,以前若是自行添加过全局的Omit
会致使报错:Duplicate identifier 'Omit'.
app
两种解决方法:编辑器
Omit
,只用lib.d.ts
提供的Omit
Omit
改成export
而非放到全局TypeScript有一个特性,叫作对象文本中的多余属性检查,其有助于避免 typoide
TypeScript 3.4 及以前,这一功能有一些问题,例以下面的例子不会报错函数
type Point = {
x: number;
y: number;
};
type Label = {
name: string;
};
const thing: Point | Label = {
x: 0,
y: 0,
name: true // uh-oh!
};
复制代码
在 TypeScript 3.5 中,类型检查器至少验证所提供的属性属于某个 union 成员,而且具备适当的类型,来保证上面的例子会报错。
目前没有发现这个优化会产生问题,但若是这项优化致使了代码检查不经过的问题,可使用这两种方法解决:
{ myProp: SomeType } as ExpectedType
)interface ExpectedType { myProp: SomeType; [prop: string]: unknown }
)--allowUmdGlobalAccess
flagallowUmdGlobalAccess
标志将容许从任何地方访问 UMD 模块中定义的全局,包括从 modules 中访问。这多是不安全的,由于并非全部 UMD 模块都在模块系统中定义全局,可是在某些状况下,您可能确实须要这种能力。例如,在浏览器环境中,一个UMD模块须要在 require.js
以前加载。该模块所以只能经过全局的方式来访问。所以,若是你确实遇到了这种状况,能够用这个标志来摆脱困境
type S = { done: boolean, value: number }
type T =
| { done: false, value: number }
| { done: true, value: number };
declare let source: S;
declare let target: T;
target = source;
复制代码
在TypeScript 3.5以前,上述检查将会失败,由于 S
既不能赋值给 { done: false, value: number }
也不能赋值给 { done: true, value: number }
。这种设计有时会避免一些 bug:
interface Foo {
kind: "foo";
value: string;
}
interface Bar {
kind: "bar";
value: number;
}
function doSomething(x: Foo | Bar) {
if (x.kind === "foo") {
x.value.toLowerCase();
}
}
// uh-oh - luckily TypeScript errors here!
doSomething({
kind: "foo",
value: 123,
});
复制代码
但这种设计仍是比较诡异,在TypeScript 3.5中,当用T这样的区别属性分配类型时,语言会更进一步,将S
这样的类型分解为每一个可能的类型的 union。在本例中,因为boolean
是true
和false
的联合,因此S
将被视为{done: false, value: number} | {done: true, value: number}
TypeScript 3.4 改进了「返回函数的泛型函数」的类型推断:
function arrayify<T>(x: T): T[] {
return [x];
}
type Box<U> = { value: U }
function boxify<U>(y: U): Box<U> {
return { value: y };
}
let newFn = compose(arrayify, boxify);
复制代码
在 TypeScript 3.4 以前的版本,newFN
将是 (x: {}) => Box<{}[]>
,在 TypeScript 3.4 以后,newFn
将是 <T>(x: T) => Box<T[]>
TypeScript 3.5 将这种行为带到了构造函数上,在某些 UI 库(如React)中操做类组件的函数能够更正确地操做泛型类组件:
type ComponentClass<P> = new (props: P) => Component<P>;
declare class Component<P> {
props: P;
constructor(props: P);
}
declare function myHoc<P>(C: ComponentClass<P>): ComponentClass<P>;
type NestedProps<T> = { foo: number, stuff: T };
declare class GenericComponent<T> extends Component<NestedProps<T>> {
}
// type is 'new <T>(props: NestedProps<T>) => Component<NestedProps<T>>'
const GenericComponent2 = myHoc(GenericComponent);
复制代码
unknown
之前泛型参数的隐式约束是空对象类型{}
。在 TypeScript 3.5 中,没有显式约束的泛型类型参数如今隐式地约束为unknown
。
{}
与 unknown
有下述不一样:
{}
能够被相似这样k["foo"]
访问(虽然在 --noImplicitAny
下会报错){}
不能被赋值为 null
和 undefined
,但 unknown
能够{}
能够被赋值给object
,但 unknown
能够调用时,这意味着相似 T.toString()
等会报错:
function foo<T>(x: T): [T, string] {
return [x, x.toString()]
// 会报错:T 里面没有 toString 属性
}
// 解决方法:增长显式约束
function foo<T extends {}>(x: T): [T, string] {
return [x, x.toString()]
}
复制代码
对泛型参数的类型推断失败时,返回的类型一样会从 {}
变成 unknown
function parse<T>(x: string): T {
return JSON.parse(x);
}
// k has type 'unknown' - previously, it was '{}'.
const k = parse("...");
// 解决方法:
// 'k' now has type '{}'
const k = parse<{}>("...");
复制代码
{ [k: string]: unknown }
再也不是任意对象类型的有效赋值目标TypeScript中的索引签名 { [s: string]: any }
的行为很特别:它是任何对象类型的有效赋值目标。这是一个特殊的规则,由于带有索引签名的类型一般不会产生这种行为。
在以前 unknown
在这种状况下的表现与 any
相同 ,{ [s: string]: unknown } 一样是任意对象类型的有效赋值目标
let dict: { [s: string]: unknown };
// Was okay
dict = () => {};
复制代码
通常来讲,这个规则是有意义的——隐含的约束「它的全部属性都是unknown
的某个子类型」对于任何对象类型都是很是正确的。然而,在TypeScript 3.5中,{[s: string]: unknown} 这个特殊规则被删掉了,缘由是上面的变动:
对泛型参数的类型推断失败时,返回的类型一样会从
{}
变成unknown
declare function someFunc(): void;
declare function fn<T>(arg: { [k: string]: T }): void;
fn(someFunc);
复制代码
在TypeScript 3.4中,依次发生以下状况:
T
找不到可选类型T
被视为{}
someFunc
不能赋值给 arg
,由于没有任何特殊规则,容许给 {[k: string]:{}}
随意赋值因为对泛型参数的类型推断失败时,其类型从 {}
变成 unknown
,arg
的类型将变成{[k: string]: unknown}
,任何东西均可以赋值给它,所以调用将被错误地容许。因此 TypeScript 3.5 中,{ [k: string]: unknown }` 再也不是任意对象类型的有效赋值目标。
注意:普通的对象字面量不受影响:
const obj = { m: 10 };
// okay
const dict: { [s: string]: unknown } = obj;
复制代码
以前的 { [s: string]: unknown }
,根据预期行为,可使用几种替代方法:
{ [s: string]: any }
{ [s: string]: {} }
object
unknown
any
TypeScript 3.4 下,如下逻辑不会报错
type A = {
s: string;
n: number;
};
const a: A = { s: "", n: 0 };
function write<K extends keyof A>(arg: A, key: K, value: A[K]): void {
// ???
arg[key] = "hello, world";
}
// Breaks the object by putting a string where a number should be
write(a, "n", "oops");
复制代码
在TypeScript 3.5中,这个逻辑被修复,上面的示例正确地发出了一个错误。
这种错误的大多数表示相关代码中有潜在错误。若是确定没错,则能够强行使用一发类型断言。
Object.keys
将拒绝原始值类型ES5 环境下,若是调用 Object.keys 时传入一个非对象参数,会抛出错误,但ES2015中,若是传入的参数是原始类型, Object.keys 将返回 []
。
以前 TypeScript 没注意到这种状况,这会致使 ES5 环境下可能出问题,如今若是发现目标环境是 ES5,向 Object.keys
传入原始类型,会报错。
因此下述状况下,有可能须要额外添加类型断言:
function fn(arg: object | number, isArgActuallyObject: boolean) {
if (isArgActuallyObject) {
const k = Object.keys(arg as object);
}
}
复制代码
由于:
函数的泛型参数将被隐式约束为
unknown
因此下面的调用也可能所以报错
declare function fn<T>(): T;
// Was okay in TypeScript 3.4, errors in 3.5 under --target ES5
Object.keys(fn());
复制代码
TypeScript 3.5 让 vscode 支持下图的功能了:
TypeScript 3.5 让 vscode 支持下图的功能了:
咱们预计3.6将带来更好的创做和使用 generators 的体验,支持ECMAScript的 private fields proposal,以及为「支持快速增量构建和项目引用的构建工具」提供新 api。
自 3.6 起,更新频率将从每 2 个月发一版变为每 3 个月发一版。
Happy hacking!
– Daniel Rosenwasser and the TypeScript team