做者:Marius Schulz
译者:前端小智
来源: https://mariusschulz.com/
点赞再看,养成习惯本文
GitHub
https://github.com/qq44924588... 上已经收录,更多往期高赞文章的分类,也整理了不少个人文档,和教程资料。欢迎Star和完善,你们面试能够参照考点复习,但愿咱们一块儿有点东西。前端
TypeScript 2.8
容许我们在每一个文件的基础上指定JSX
工厂名。在早期版本,只能经过--jsxFactory
编译器选项指定JSX
工厂名。此设置适用于整个项目中的每一个JSX文件。如今,我们还能够经过在文件的开头添加一个特殊的@jsx
注释来覆盖项目范围的--jsxFactory
设置。node
假设我们要使用Preact渲染字符串 "Hello World!"
并放入<div id="app">
容器中。 Preact 使用h
函数来建立 JSX 元素。 我们能够在.tsx
文件的开头添加特殊的/ ** @jsx h */
注释(也称为“pragma”):react
有了/** @jsx h */
编译指示后,编译器将为上述文件生成如下 JS 代码:git
/** @jsx h */ import { h, render } from "preact"; render( h("h1", null, "Hello World!"), document.getElementById("app") );
下面是用来编译代码的tsconfig.json
配置文件:github
{ "compilerOptions": { "target": "es5", "module": "es2015", "moduleResolution": "node", "jsx": "react", "strict": true } }
请注意,若是您使用/** ... */
块注释语法,则编译器仅识别编译指示。 若是使用// ...
单行注释语法,则不会更改JSX出厂设置。面试
JSX不是 ECMAScript 标准的一部分;也就是说,它自己不是有效的 JS。所以,包含JSX
的脚本或模块不能直接在浏览器中运行。与带有类型注释的文件同样,JSX
文件首先须要编译成纯 JS 文件。--jsxFactory
选项告诉 TypeScript 编译器应该如何编译JSX
元素。typescript
注意 <h1> Hello World!</h1>
如何转换为 h("h1", null, "Hello World!")
。 Preact
使用函数h
建立虚拟 DOM 元素,这就是为何我们将h
指定为JSX工厂名称的缘由。 咱们还须要从preact
包中导入h
,以便它在模块中可用。json
那么,何时须要在每一个文件的基础上指定JSX工厂呢?若是我们在项目中只将JSX
与单个 JS库一块儿使用,则不须要对每一个文件进行配置。在这种状况下,更容易在tsconfig
中更改--jsxFactory
选项。这样它就能够应用于项目中的全部JSX
文件:数组
{ "compilerOptions": { "target": "es5", "module": "es2015", "moduleResolution": "node", "jsx": "react", "jsxFactory": "h", "strict": true } }
默认状况下,使用--jsx react
选项时,--jsxFactory
选项设置为React.createElement
。 所以,若是我们使用的是 React,则彻底不须要指定--jsxFactory
选项,也没必要添加/ ** @jsx ... * /
编译指示。浏览器
若是在同一项目中将多个JS库与JSX一块儿使用,则JSX工厂的按文件配置颇有用。 例如,我们可能想将Vue组件添加到主要用 eact 编写的Web应用程序中。 / ** @jsx ... * /
编译指示容许我们为这些文件指定不一样的 JSX 工厂,而没必要具备多个tsconfig.json
文件。
TypeScript 2.8 引入了有条件类型,这是类型系统的强大而使人兴奋的补充。 有条件类型使我们能够表达非均匀类型映射,即,根据条件而不一样的类型转换。
有条件的类型会以一个条件表达式进行类型关系检测,从而在两种类型中选择其一:
T extends U ? X : Y
上面的类型意思是,若T
可以赋值给U
,那么类型是X
,不然为Y
。
下面是一个在 TypeScript 的lib.es5.d.ts
类型定义文件中预约义的有条件类型的例子
/** * Exclude null and undefined from T */ type NonNullable<T> = T extends null | undefined ? never : T;
若是类型T
可赋值给类型null
或类型undefined
,则NonNullable<T>
类型为never
类型;不然它将保留类型 T
。never
类型是 TypeScript 的底层类型,表示从未出现的值的类型。
那么,为何e 条件类型和never
类型的组合是有用的呢?它有效地容许我们从联合类型中删除组成类型。若是有条件类型里待检查的类型是naked type parameter
,那么它也被称为“分布式有条件类型”。 分布式有条件类型在实例化时会自动分发成联合类型。 例如,实例化T extends U ? X : Y
,T
的类型为A | B | C
,会被解析为(A extends U ? X : Y) | (B extends U ? X : Y) | (C extends U ? X : Y)
。
这个描述至关抽象,我们来看一个具体的例子。我们定义一个EmailAddress
类型别名,它表示四种不一样类型的联合,包括null
和undefined
类型
type EmailAddress = string | string[] | null | undefined;
如今,我们将NonNullable<T>
类型应用于EmailAddress
,并逐步解析结果类型:
type NonNullableEmailAddress = NonNullable<EmailAddress>;
我们首先用别名的联合类型替换EmailAddress
:
type NonNullableEmailAddress = NonNullable< | string | string[] | null | undefined >;
这就是有条件类型的分配特性发挥做用的地方。NonNullable<T>
类型应用于联合类型,这至关于将有条件类型应用于联合类型中的全部类型:
type NonNullableEmailAddress = | NonNullable<string> | NonNullable<string[]> | NonNullable<null> | NonNullable<undefined>;
如今,我们能够在任何地方经过其定义替换NonNullable<T>
:
type NonNullableEmailAddress = | (string extends null | undefined ? never : string) | (string[] extends null | undefined ? never : string[]) | (null extends null | undefined ? never : null) | (undefined extends null | undefined ? never : undefined);
接下来,我们必须解析这四种有条件类型。string
和string[]
都不能赋值给 null | undefined
,这就是前两种类型选择string
和string[]
的缘由。null
和undefined
均可以赋值给null | undefined
,这就是为何后两种类型都选择never
:
type NonNullableEmailAddress = | string | string[] | never | never;
由于never
是每一个类型的子类型,因此能够从联合类型中省略它:
type NonNullableEmailAddress = string | string[];
这就是咱们指望的类型。
如今让我们看一个更复杂的例子,它将映射类型与条件类型组合在一块儿。这里,咱们定义了一个类型,它从一个类型中提取全部不可为空的属性键
type NonNullablePropertyKeys<T> = { [P in keyof T]: null extends T[P] ? never : P }[keyof T];
这种类型乍一看彷佛至关神秘。再一次,将经过查看一个具体的示例并逐步解析获得的类型来尝试揭开它的神秘面纱。
假设我们有一个User
类型,想要使用NonNullablePropertyKeys<T>
类型来找出哪些属性是不可空的:
type User = { name: string; email: string | null; }; type NonNullableUserPropertyKeys = NonNullablePropertyKeys<User>;
下面是我们如何解析NonNullablePropertyKeys<User>
。首先,我们将User
类型做为T
类型参数的类型参数提供:
type NonNullableUserPropertyKeys = { [P in keyof User]: null extends User[P] ? never : P }[keyof User];
其次,我们在映射类型中解析keyof User
。User
类型有两个属性,name
和email
,所以我们最终获得一个带有“name”
和“email”
字符串字面量类型的联合类型:
type NonNullableUserPropertyKeys = { [P in "name" | "email"]: null extends User[P] ? never : P }[keyof User];
接下来,咱们将在映射中展开P in...
,并将P
类型替换为“name”
和“email”
type NonNullableUserPropertyKeys = { name: null extends User["name"] ? never : "name"; email: null extends User["email"] ? never : "email"; }[keyof User];
而后,经过查找User
中的name
和email
属性的类型,我们能够继续并解析索引访问类型User["name"]
和User["email"]
:
type NonNullableUserPropertyKeys = { name: null extends string ? never : "name"; email: null extends string | null ? never : "email"; }[keyof User];
如今是应用条件类型的时候了。null
不扩string
,但它确实扩展了string | null
,所以我们分别以“name”
和never
类型结束:
type NonNullableUserPropertyKeys = { name: "name"; email: never; }[keyof User];
如今我们已经完成了映射类型和条件类型,再一次,我们将解析keyof Use
:
type NonNullableUserPropertyKeys = { name: "name"; email: never; }["name" | "email"];
我们如今有一个索引访问类型,它查找name
和email
属性的类型。TypeScript 经过逐个查找每一个类型并建立联合类型来解决这个问题:
type NonNullableUserPropertyKeys = | { name: "name"; email: never }["name"] | { name: "name"; email: never }["email"];
如今,我们能够在两个对象类型中查找name
和email
属性。name
属性的类型是“name”
,而email
属性的类型是“never”
:
type NonNullableUserPropertyKeys = | "name" | never;
和前面同样,我们能够经过清除never
类型来简化生成的联合类型:
type NonNullableUserPropertyKeys = "name";
User
类型中惟一不可为空的属性键是“name”
。
我们进一步研究这个示例,并定义一个类型来提取给定类型的全部不可空属性。咱们能够将Pick <T,K>
类型用于lib.es5.d.ts
中预约义的类型:
/** * From T, pick a set of properties * whose keys are in the union K */ type Pick<T, K extends keyof T> = { [P in K]: T[P]; };
我们能够结合NonNullablePropertyKeys<T>
类型和Pick<T, K>
来定义NonNullableProperties<T>
,这是我们要查找的类型:
type NonNullableProperties<T> = Pick<T, NonNullablePropertyKeys<T>>; type NonNullableUserProperties = NonNullableProperties<User>; // { name: string }
实际上,这是我们指望的类型:在User
类型中,只有name
属性不可空。
有条件类型支持的另外一个有用特性是使用新的infer
关键字推断类型变量。在有条件类型的extends
子句中,可使用新的infer
关键字来推断类型变量,从而有效地执行类型上的模式匹配
type First<T> = T extends [infer U, ...unknown[]] ? U : never; type SomeTupleType = [string, number, boolean]; type FirstElementType = First<SomeTupleType>; // string
注意,推断的类型变量(在本例中为U
)只能在条件类型的true
分支中使用。
TypeScript 一个长期存在的特性要求是可以提取给定函数的返回类型。下面是ReturnType<T>
类型的简化版本,该类型是在lib.es5.d.ts
中预约义的。它使用infer
关键字来推断函数类型的返回类型:
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : any; type A = ReturnType<() => string>; // string type B = ReturnType<() => () => any[]>; // () => any[] type C = ReturnType<typeof Math.random>; // number type D = ReturnType<typeof Array.isArray>; // boolean
注意,我们必须使用typeof
来得到Math.random()
和Array.isArray()
方法的返回类型。我们须要传递类型做为类型参数T
的参数,而不是值;这就是为何ReturnType<Math.random>
和ReturnType<Array.isArray>
是不正确的。
TypeScript 2.8 在lib.d.ts
里增长了一些预约义的有条件类型:
Exclude<T, U>
-- 从T中剔除能够赋值给U的类型。Extract<T, U>
-- 提取T中能够赋值给U的类型。NonNullable<T>
-- 从T中剔除null和undefined。ReturnType<T>
-- 获取函数返回值类型。InstanceType<T>
-- 获取构造函数类型的实例类型。Exclude<T, U>
从T
中剔除能够赋值给U
的类型。
定义:
/** * Exclude from T those types that are assignable to U */ type Exclude<T, U> = T extends U ? never : T;
事例:
type A = Exclude<string | string[], any[]>; // string type B = Exclude<(() => void) | null, Function>; // null type C = Exclude<200 | 400, 200 | 201>; // 400 type D = Exclude<number, boolean>; // number
经过Extract <T,U>
类型,提取T
中能够赋值给U
的类型。
定义:
/** * Extract from T those types that are assignable to U */ type Extract<T, U> = T extends U ? T : never;
事例:
type A = Extract<string | string[], any[]>; // string[] type B = Extract<(() => void) | null, Function>; // () => void type C = Extract<200 | 400, 200 | 201>; // 200 type D = Extract<number, boolean>; // never
我们上面使用了NonNullable<T>
类型,该类型从T
中过滤出null
和undefined
类型。
定义:
/** * Exclude null and undefined from T */ type NonNullable<T> = T extends null | undefined ? never : T;
事例:
type A = NonNullable<boolean>; // boolean type B = NonNullable<number | null>; // number type C = NonNullable<string | undefined>; // string type D = NonNullable<null | undefined>; // never
注意,空类型D
是如何由never
表示的。
ReturnType<T>
获取函数返回值类型。
定义:
/** * Obtain the return type of a function type */ type ReturnType<T extends (...args: any[]) => any> = T extends (...args: any[]) => infer R ? R : any;
事例:
type A = ReturnType<() => string>; // string type B = ReturnType<() => () => any[]>; // () => any[] type C = ReturnType<typeof Math.random>; // number type D = ReturnType<typeof Array.isArray>; // boolean
Parameters<T>
获取构造函数类型的实例类型。
定义:
/** * Obtain the parameters of a function type in a tuple */ type Parameters<T extends (...args: any[]) => any> = T extends (...args: infer P) => any ? P : never;
注意,Parameters<T>
类型在结构上与ReturnType<T>
类型几乎相同。 主要区别在于infer
关键字的位置。
事例:
type A = Parameters<() => void>; // [] type B = Parameters<typeof Array.isArray>; // [any] type C = Parameters<typeof parseInt>; // [string, (number | undefined)?] type D = Parameters<typeof Math.max>; // number[]
Array.isArray()
方法刚好须要一个任意类型的参数。 这就是为何将B
类型解析为[any]
,即具备一个元素的元组的缘由。 另外一方面,Math.max()
方法指望任意多个数值参数(而不是单个数组参数);所以,类型D
被解析为number[](
而不是[number []]
)。
InstanceType<T>
类型提取构造函数类型的返回类型,它至关于构造函数的ReturnType<T>
。
定义
/** * Obtain the return type of a constructor function type */ type InstanceType<T extends new (...args: any[]) => any> = T extends new (...args: any[]) => infer R ? R : any;
再次注意InstanceType <T>
类型在结构上与ReturnType <T>
和ConstructorParameters <T>
类型很是类似。
事例:
type A = InstanceType<ErrorConstructor>; // Error type B = InstanceType<FunctionConstructor>; // Function type C = InstanceType<RegExpConstructor>; // RegExp
代码部署后可能存在的BUG无法实时知道,过后为了解决这些BUG,花了大量的时间进行log 调试,这边顺便给你们推荐一个好用的BUG监控工具 Fundebug。
原文:
https://mariusschulz.com/blog...
https://mariusschulz.com/blog...
https://mariusschulz.com/blog...
文章每周持续更新,能够微信搜索「 大迁世界 」第一时间阅读和催更(比博客早一到两篇哟),本文 GitHub https://github.com/qq449245884/xiaozhi 已经收录,整理了不少个人文档,欢迎Star和完善,你们面试能够参照考点复习,另外关注公众号,后台回复福利,便可看到福利,你懂的。