【速查手册】TypeScript 高级类型 cheat sheet

学习 TypeScript 到一定阶段,必须要学会高阶类型的使用,否则一些复杂的场景若是用 any 类型来处理的话,也就失去了 TS 类型检查的意义。本文罗列了 TypeScript 常用的高阶类型,包含 官方 、以及 常用的非官方 的高级类型声明,该手册直接硬啃的话有些枯燥,适合平时快速查阅,使用 Ctrl+F 来查找关键词来定位即可。官方文档 - 高级类型:优先阅读,建议阅读英文文档。附 中文文档,有人做了专门的读书笔记 Typescript学习记录:高级类型TypeScript: Built-in generic types:推荐,用案例详细解释高阶类型的使用;TS 一些工具泛型的使用及其实现:TS 内置工具泛型高阶使用TypeScript 2.1 新特性一览:查找/映射类型及 any 类型的推断 都是在 2.1 版本引入的TypeScript 2.8:Exclude 等条件类型是在 2.8 版本引入的,附中文 TypeScript 2.8 引入条件类型lib.es2015.d.ts:大部分的声明在这个文件中可以找到TypeScript 强大的类型别名:行文结构比较合理,也比较完善,可以当手册来查1、基础1.1、交叉类型交叉类型是将 多个类型合并为一个类型。 这让我们可以把现有的多种类型叠加到一起成为一种类型,它包含了所需的所有类型的特性。Person & Serializable & Loggable同时是 Person 和 Serializable 和 Loggable。就是说这个类型的对象同时拥有了这三种类型的成员示例:extend 融合方法function extend<T, U>(first: T, second: U): T & U { let result = <T & U>{}; for (let id in first) { (<any>result)[id] = (<any>first)[id]; } for (let id in second) { if (!result.hasOwnProperty(id)) { (<any>result)[id] = (<any>second)[id]; } } return result;}特殊情况:T | never = TT & never = never (which #16446 provides)1.2、extends 关键字T extends U ? X : Y表示,如果 T 可以赋值给 U (类型兼容),则返回 X,否则返回 Y;1.3、使用 keyof 和 inkeyof 可以用来取得一个对象接口的所有 key 值:interface Foo { name: string; age: number}type T = keyof Foo // -> “name” | “age"而 in 则可以遍历枚举类型, 例如:type Keys = “a” | “b"type Obj = { [p in Keys]: any} // -> { a: any, b: any }keyof 产生联合类型, in 则可以遍历枚举类型, 所以他们经常一起使用。1.4、infer 关键字infer 这个关键字是在 TS 2.8 版本引入的, 在条件类型语句中,该关键字用于替代手动获取类型。TypeScript 为此提供了一个示例,他们创建了一个叫作 Flatten 的类型,用于将数组转成他们需要的元素类型:type Flatten<T> = T extends any[] ? T[number] : T;如果使用关键字 infer 就可以将上面的代码简化成:type Flatten<T> = T extends Array<infer U> ? U : T;2、映射类型2.1、Partial(官方)作用:将传入的属性变为可选项源码:type Partial<T> = { [P in keyof T]?: T[P] };解释:keyof T 拿到 T 所有属性名然后 in 进行遍历, 将值赋给 P, 最后 T[P] 取得相应属性的值.结合中间的 ? 我们就明白了 Partial 的含义了.扩展:内置的 Partial 有个局限性,就是只支持处理第一层的属性,如果是嵌套多层的就没有效果了,不过可以如下自定义:type PowerPartial<T> = { // 如果是 object,则递归类型 [U in keyof T]?: T[U] extends object ? PowerPartial<T[U]> : T[U]};2.2、Required(官方)作用:将传入的属性变为必选项源码:type Required<T> = { [P in keyof T]-?: T[P] };解释:我们发现一个有意思的用法 -?, 这里很好理解就是将可选项代表的 ? 去掉, 从而让这个类型变成必选项与之对应的还有个 +? , 这个含义自然与 -? 之前相反, 它是用来把属性变成可选项的2.3、Readonly(官方)作用:将传入的属性变为只读选项源码:type Readonly<T> = { readonly [P in keyof T]: T[P] };扩展:在 巧用 Typescript 中,作者创建了 DeepReadonly 的声明,使用 递归 的思想让任何子属性都不可更改type DeepReadonly<T> = { readonly [P in keyof T]: DeepReadonly<T[P]>;}const a = { foo: { bar: 22 } }const b = a as DeepReadonly<typeof a>b.foo.bar = 33 // Hey, stop!2.4、Mutable(第三方)作用:将 T 的所有属性的 readonly 移除源码:type Mutable<T> = { -readonly [P in keyof T]: T[P]}解释:这一对加减符号操作符 + 和 -, 进行的不是变量的之间的进行加减而是对 readonly 属性进行加减2.5、Record(官方)作用:将 K 中所有的属性的值转化为 T 类型源码:type Record<K extends keyof any, T> = { [P in K]: T };示例:// 对所有 T 类型的属性 K, 将它转换为 Ufunction mapObject<K extends string | number, T, U>(obj: Record<K, T>, f: (x: T) => U): Record<K, U>;const names = { foo: “hello”, bar: “world”, baz: “bye” };const lengths = mapObject(names, s => s.length); // { foo: number, bar: number, baz: number }2.6、Pick(官方)作用:从 T 中取出 一系列 K 的属性源码:type Pick<T, K extends keyof T> = { [P in K]: T[P] };示例:// 从 T 挑选一些属性 Kdeclare function pick<T, K extends keyof T>(obj: T, …keys: K[]): Pick<T, K>;const nameAndAgeOnly = pick(person, “name”, “age”); // { name: string, age: number }3、条件类型3.1、Exclude(官方)某些地方也称为 Diff作用:从 T 中剔除可以赋值给 U 的类型,换言之就是从T 中排除 U源码:type Exclude<T, U> = T extends U ? never : T;解释:在 ts 2.8 中引入了一个条件类型, T extends U ? X : Y 表示如果 T 是 U 的子类型的话,那么就会返回 X,否则返回 Y对于联合类型来说会自动分发条件,例如 T extends U ? X : Y, T 可能是 A | B 的联合类型, 那实际情况就变成(A extends U ? X : Y) | (B extends U ? X : Y)示例:type T = Exclude<1 | 2, 1 | 3> // -> 2参考文档:Add support for literal type subtractionTypeScript在React高阶组件中的使用技巧3.2、Extract(官方)作用:从 T 中提取出包含在 U 的类型,换言之就是从T 中提取出 U 子集源码:type Extract<T, U> = T extends U ? T : never;示例:type T = Extract<1 | 2, 1 | 3> // -> 13.3、Omit (第三方)作用:从 T 中忽略在 K 中的属性名 ,实现忽略对象某些属性功能,多在高阶组件中使用源码:type Omit<T, K> = Pick<T, Exclude<keyof T, K>>示例:type Foo = Omit<{name: string, age: number}, ’name’> // -> { age: number }3.4、Overwrite(第三方)作用: T 中的定义被在 K 中的内容所覆盖,多在高阶组件中使用,内部借助 Diff 操作实现源码:type Overwrite<T, U> = { [P in Exclude<keyof T, keyof U>]: T[P] } & U;示例:type Item1 = { a: string, b: number, c: boolean };type Item2 = { a: number };type T3 = Overwrite<Item1, Item2> // { a: number, b: number, c: boolean };3.5、ReturnType (官方)作用:从 T 中忽略在 K 中的属性名 ,实现忽略对象某些属性功能,多在高阶组件中使用源码:type ReturnType<T> = T extends ( …args: any[]) => infer R ? R : any;解释:我们可以用 infer 声明一个类型变量,是用它获取函数的返回类型,简单说就是用它取到函数返回值的类型方便之后使用.示例:function foo(x: number): Array<number> { return [x];}type fn = ReturnType<typeof foo>;4、函数相关4.1、ThisType(官方)作用:用于指定上下文对象类型的源码:// node_modules/typescript/lib/lib.es5.d.tsinterface ThisType<T> { }解释:可以看到声明中只有一个接口,没有任何的实现说明这个类型是在 TS 源码层面支持的,而不是通过类型变换。示例:interface Person { name: string; age: number;}const obj: ThisType<Person> = { dosth() { this.name // string }}这样的话,就可以指定 obj 里的所有方法里的上下文对象改成 Person 这个类型了4.2、InstanceType(官方)作用:用于获取构造函数类型的实例类型源码:// node_modules/typescript/lib/lib.es5.d.tstype InstanceType<T extends new (…args: any[]) => any> = T extends new (…args: any[]) => infer R ? R : any;解释:使用 infer 和 extends 条件判断完成示例:class C { x = 0; y = 0;}type T20 = InstanceType<typeof C>; // Ctype T21 = InstanceType<any>; // anytype T22 = InstanceType<never>; // anytype T23 = InstanceType<string>; // Errortype T24 = InstanceType<Function>; // Error这样的话,就可以指定 obj 里的所有方法里的上下文对象改成 Person 这个类型了4.3、NonNullable(官方)作用:这个类型可以用来过滤类型中的 null 及 undefined 类型。源码:// node_modules/typescript/lib/lib.es5.d.tstype NonNullable<T> = T extends null | undefined ? never : T;解释:使用 extends 条件判断完成示例:type T22 = string | number | null;type T23 = NonNullable<T22>; // -> string | number;4.4、Parameters(官方)作用:该类型可以获得函数的参数类型组成的元组类型。源码:// node_modules/typescript/lib/lib.es5.d.tstype Parameters<T extends (…args: any[]) => any> = T extends (…args: infer P) => any ? P : never;解释:使用 infer 和 extends 条件判断完成示例:function foo(x: number): Array<number> { return [x];}type P = Parameters<typeof foo>; // -> [number]此时 P 的真实类型就是 foo 的参数组成的元组类型 [number]4.5、ConstructorParameters(官方)作用:获得类的参数类型组成的元组类型。源码:// node_modules/typescript/lib/lib.es5.d.tstype ConstructorParameters<T extends new (…args: any[]) => any> = T extends new (…args: infer P) => any ? P : never;解释:使用 infer 和 extends 条件判断完成示例:class Person { private firstName: string; private lastName: string; constructor(firstName: string, lastName: string) { this.firstName = firstName; this.lastName = lastName; }}type P = ConstructorParameters<typeof Person>; // -> [string, string]此时 P 就是 Person 中 constructor 的参数 firstName 和 lastName 的类型所组成的元组类型 [string, string]。 ...

April 1, 2019 · 5 min · jiezi

CSS Flexbox 可视化手册

翻译:疯狂的技术宅原文:https://medium.com/swlh/css-f…本文首发微信公众号:jingchengyideng欢迎关注,每天都给你推送新鲜的前端技术文章介绍Flexbox是 Flexible Box Module(弹性盒模型)的缩写。 是一种可以轻松控制html元素之间的空间分布和对齐的布局模型。Flexbox同一时间只能控制行或列中的一个维度。对于二维控制需要 CSS 网格布局。首先给出如下模板:<body> <div class=“container”> <div class=“box box-1”>1</div> <div class=“box box-2”>2</div> <div class=“box box-3”>3</div> <div class=“box box-4”>4</div> <div class=“box box-5”>5</div> <div class=“box box-6”>6</div> <div class=“box box-7”>7</div> <div class=“box box-8”>8</div> <div class=“box box-9”>9</div> <div class=“box box-10”>10</div> </div></body>以上 div 的默认行为遵循普通的html文档流,将会从上到下、从左到右呈现,并采用整个 body 的宽度,因为其 display 属性默认为block。弹性项目当 display: flex 应用于 .container div 时,所有直接子div都变为 flex-items,并获得新的行为它们将显示在同一行中,因为flex-direction默认为row它们将会从左到右显示其中的项目不会自动伸展来适应整个宽度(主轴),为了做到这一点,它们会缩小。项目会被拉伸以适合交叉轴(在此示例中为高度)。 如果这些项目的高度不一致,它们将会伸展到最高的那个高度flex-basis 默认为 auto(项目宽度将由其内容决定)flex-wrap 默认为nowrap(如果容器的宽度不足以适合这些项目,它们不会换行,而是会溢出)出于可视化的目的,让我们拉伸容器使其占据整个高度。弹性容器display:flex 使容器扩展至整个可用宽度。 这点与 display:inline-flex 相反,它使容器缩小到内容的宽度。弹性方向一旦被声明为 flex 容器,就可以认为该元素具有两个轴:主轴与交叉轴。 主轴由flex-direction属性定义。 交叉轴垂直于前者。flex-direction 属性有四个值:row,row-reverse,column 和 column-reverse。其默认值为row,它从左到右水平设置主轴,交叉轴从上到下垂直截取。 类似地, column 值从顶部到底部垂直设置主轴,从左到右设置交叉轴。 这两个选项的相反属性使主轴反转180°。 交叉轴保持不变。可以通过下图观察这些值的 flex-items 行为:Flex Wrap当容器中的空间不足以容纳其中的弹性项目时,可以用 flex-wrap 来处理。在默认情况下,flex-wrap 被设置为 nowrap,这意味着如果容器不能适应在其内的行中原始宽度的项目,则这些项目将会缩小来进行适应。 如果它们因为某种原因无法收缩,则会溢出容器。把项目宽度设置为300px,nowrap 选项会输出以下结果:其中,每个项目都会缩小到大约 70px 来适合容器。当属性被更新为wrap时,现在项目的宽度实际上是原始值300px。 当第一行不足以容纳300px时,则该项目将换行到新的一行,而不是溢出容器。 应该把其中的每一行都视为单独的弹性容器。 一个容器中的空间分布不会影响到与其相邻的其他容器。但是为什么弹性项目会占据整个屏幕高度呢? 在第一部分中,容器高度设置为 100vh ,因此可用空间被平均分为四行,来适合 300px 项目的需要。 如果我们没有设置 100vh,容器的高度则会遵循项目内容的高度,如下图所示:另一个选项是wrap-reverse,它会反转交叉轴。 通过 flex-direction 属性从上到下设置,wrap-reverse 将其转换为从下到上。通过使用 flex-direction:column 反转主轴,不适应的元素会被换到另一列,剩余空间被均匀分割。wrap-reverse 选项会沿着列方向将交叉轴从右向左反转,产生以下输出:由于flexbox是单维度布局,所以在进行反转时,项目从下到上进行排列(对于行方向),但保持左右结构,只改变了交叉轴。弹性流flex-direction 和 flex-wrap 可以在一个属性当中声明:flex-flow:[direction][wrap] 。.flex-container { flex-flow : column wrap;}项目之间的缝隙让我们回到row/wrap。 可以通过设置项目的 width:33.3333% 来填充整个容器:但是如果你希望在子div 之间有一个间隙,它们就不会按照你想的那样换行:这个小麻烦这可以通过 CSS 函数 calc() 来解决:.flex-item { width: calc(33.33333% - 40px); margin: 20px;}为了消除容器边缘的空间,可以在容器上使用负边距:.flex-container { margin: -20px;}排序order 属性允许更改出现的可视排序项目。排序被分配给组。 默认情况下所有的弹性项目都设置为 order: 0,这意味着所有项目都属于同一组,并且它们将按照原始顺序定位。 在两个或多个组的情况下,组会相对于它们的整数值进行排序。在下面的例子中,有三个 ordinal groups:-1, 0和 1,按此顺序进行排列。.box-3 { order: 1; }.box-7 { order: 1; }.box-8 { order: -1; }此属性可视地重新分配项目,但在交互时保持其原始源位置,例如使用Tab键遍历它们。 如果物品订购对可访问性有影响,则可以考虑这一点。 flex-direction 也是如此。对齐(此图反复上传总是出错,请大家移步原文查看)在Flexbox中,沿着轴的项目对齐和空间分布可以受到四个属性的控制:justify-content: 对齐主轴中的所有项目align-items: 对齐交叉轴中的所有项目align-self: 对齐交叉轴中的单个项目align-content: 控制交叉轴上柔性线之间的空间justify-content适用于容器,justify-content 处理项目在主轴上的对齐方式。六个最常用的选项包括: flex-start、 flex-end、center、 space-around、 space-between 和 space-evenly,flex-start是默认值。.align-items也适用于容器, align-items 属性处理交叉轴方向上的对齐。它的默认值是 stretch 其它的选项是 flex-start、flex-end、 center 和 baseline 。stretch 选项使所有项目伸展到容器高度(如果设置)或最高项目的高度[5]。 第一张图片显示容器高度设置为 100vh,未设置第二个高度。align-content这是作用在 flex 容器的四个属性中的最后一个,align-content 在交叉轴中的弹性线之间分配空格。 作为后者,它的初始值是 stretch 和 justify-content,它接受以下选项: flex-start, flex-end, center, space-around, space-between, space-evenly 。align-selfalign-items 属性实际上通过在容器内的所有 flex 项目上设置 align-self 来实现。 通过单独设置 align-self,可以覆盖全局值。 它接受与align-items和’auto’相同的值[5]。auto 选项通过 align-items 将 align-self 重置为容器全局定义的值。调整 Flexbox 的大小项目的尺寸和伸展性可以通过三种属性来控制: flex-grow、 flex-shrink 和 flex-basis。 这三个都作用于主轴。flex-grow:如果有额外的空间,每个项目应该如何放大flex-shrink:如果没有足够的空间,应该如何缩小每个项目flex-basis:在设置上述两个属性之前,该项目的大小应该是多少flex-grow由此属性设置的 flex grow factor (弹性增长因子)用来处理项目大小相对于彼此的比率。默认值为 0,这意味着如果还有可用空间,就把它放在最后一个项目之后。在上面的例子中,direction 被设置为 row,每个弹性项目的 width 被设置为 60px。 由于容器宽是 980px,所以剩余的可用空间为 680px。 这个空间被称为positive free space (正自由空间)。如果将 flex-grow 设置为1,正可用空间量会在弹性项目之间平均分配。 每个项目的宽度将会增加 136px,总宽度为196px。通过将 flex-grow: 2 应用到第三个项目,它会得到比其它项目多出两倍的可用正自由空间,即286px,其他项目仍为173px 。下图显示了把项目的 flex-grow 属性值设置为其内容对应的数字时的情形。flex-shrink当没有足够的可用空间来容纳所有容器时,用 flex-shrink 处理项目大小。 它通过缩小这些项目来划分它们之间的 negative free space (负自由空间)。下图显示的是宽度为 980px 的容器,它容纳了5个宽度为 300px的物品。 由于没有空间容纳所需的总宽度 1500px,所以默认的flex shrink factor(弹性收缩系数)的值为1,这样会使每个项目的宽度均匀缩小到196px。通过将第三项的比率设置为2,它缩小为其余项目大小的二分之一。本节的最后一张图显示了将每个项目的内容值对应的数字设定为 flex-shrink 的值时的情形。flex-basisflex-basis 是在实际设置可用空间之前,检查每个项目本来应具有的大小的属性。 默认值为 auto,项宽度由 width 属性显式设置,或者取其内容宽度。 它也接受像素值。下面的动图显示了一个800px宽的容器和五个设置为 flex-basis:160px 的弹性项目。 这告诉浏览器:如果在理想状态下,有足够的空间来放置所有的项目,就遵循它们的160px宽度,并且没有正/负可用空间;如果没有足够的空间的话,那么 flex-shrink 默认为1,所有项目均匀收缩; 如果有额外的空间,flex-grow 默认为0,并且剩余的空间放在最后一个项目之后。下一个动图展示了把项目1设置为flex-shrink:10,项目4设置为flex-grow:10。对于负自由空间,项目1的宽度减少10倍。 对于正空闲空间,第4项的宽度是其他空间的10倍。flex-basis 也接受值 content,此时无论其宽度是否被设置,计算自由空间时所考虑的宽度依据是项目中的内容。flexflex 属性是按顺序排列的 flex-grow、 flex-shrink 和 flex-basis 的简写,它接受以下预定义值:initial:重置为 flexbox 的默认值,等同于 flex: 0 1 autoauto:flex-items能够根据需要增长/缩小,等同于 flex: 1 1 autonone:固定项目,等同于 flex: 0 0 autoflex: 1:flex-items 具有伸缩的能力,flex-basis 设置为零,等同于 flex: 1 1 0Autoprefixer对于跨浏览器的兼容性问题,设置具有具有必要前缀的属性是非常重要的,以确保能够支持所有浏览器。手动自动为每个属性添加前缀可能是一项非常繁琐的任务,也使样式很难维护。使用 Gulp 能够替你自动执行这些任务。为了能够使用Gulp,我们必须将它作为依赖添加到项目当中。 这项工作是在 package.json 文件中完成的,它负责跟踪项目依赖及其版本。 在终端中输入下列命令来创建文件:nmp init系统将提示你输入项目信息,可以一直按回车键直到完成。 输出的文件内容将是这样的:{ “name”: “project-name”, “version”: “1.0.0”, “description”: “Project description”, “main”: “index.js”, “scripts”: { “test”: “echo "Error: no test specified" && exit 1” }, “author”: “Author Name”, “license”: “ISC”}全局安装gulp:npm install gulp -g安装 gulp 和 gulp-autoprefixer 作为项目依赖项:npm install gulp –save-devnpm install gulp-autoprefixer –save-dev它们将会出现在 package.json 文件中的 devDependencies 下。创建一个gulpfile.js文件:touch gulpfile.js添加以下内容:// gulpfile.jsvar gulp = require(‘gulp’);var autoprefixer = require(‘gulp-autoprefixer’);var options = { browsers: [’last 2 versions’], cascade: false };gulp.task(‘styles’, function() { return gulp.src(’./styles.css’) .pipe(autoprefixer(options)) .pipe(gulp.dest(‘build’));});gulp 会从 styles.css 中提取内容并通过 gulp-autoprefixer 传递它。 处理结果会保存在build文件夹下。参考What the Flexbox CourseBasic concepts of flexbox — 25/03/2018Mastering Wrapping of Flex Items — 26/03/2018Ordering Flex Items — 26/03/2018Aligning Items in a Flex Container — 26/03/2018StackOverflow — 27/03/2018Controlling Ratios of Flex Items Along the Main Axis — 28/03/2018GulpGulp Autoprefixer本文首发微信公众号:jingchengyideng欢迎扫描二维码关注公众号,每天都给你推送新鲜的前端技术文章 ...

January 31, 2019 · 2 min · jiezi