TS
2021-10-22 11:26:15 0 举报
AI智能生成
ts相关
作者其他创作
大纲/内容
语言类型
静态类型
弱类型: 类型系统按照「是否允许隐式类型转换」来分类
基础
布尔值、数值、字符串、空值(void,可以用 void 表示没有任何返回值的函数)、null、undefined(undefined 和 null 是所有类型的子类型)
Any(变量如果定义的时候没有赋值,不管之后有没有赋值,都会被推断成 any 类型而完全不被类型检查)
类型推论let myFavoriteNumber = 'seven';myFavoriteNumber = 7;以上会在编译的时候报错,事实上,它等价于:let myFavoriteNumber: string = 'seven';myFavoriteNumber = 7;
联合类型(Union Types — |)(我们只能访问此联合类型的所有类型里共有的属性或方法)
接口 — Interfaces(对行为的抽象,对对象的形状Shape进行描述)
interface Person { readonly id: number; // 只读属性 name: string; age?: number; // 可选属性 [propName: string]: any; // 任意属性(一旦定义了任意属性,那么确定属性和可选属性的类型都必须是它的类型的子集)}
数组的类型
「类型 + 方括号」表示法: let fibonacci: number[] = [1, 1, 2, 3, 5]
数组泛型:let fibonacci: Array<number> = [1, 1, 2, 3, 5]
用接口表示数组:interface NumberArray { [index: number]: number;}let fibonacci: NumberArray = [1, 1, 2, 3, 5]
类数组:function sum() { let args: { [index: number]: number; length: number; callee: Function; } = arguments;}事实上常用的类数组都有自己的接口定义,如 IArguments, NodeList, HTMLCollection 等(IArguments 是 TypeScript 中定义好了的类型)interface IArguments { [index: number]: any; length: number; callee: Function;}
any 在数组中的应用let list: any[] = ['xcatliu', 25, { website: 'http://xcatliu.com' }];
函数的类型
函数声明
函数表达式let mySum = function (x: number, y: number): number { return x + y;};如果需要我们手动给 mySum 添加类型,则应该是这样:(=> 用来表示函数的定义,左边是输入类型,需要用括号括起来,右边是输出类型)let mySum: (x: number, y: number) => number = function (x: number, y: number): number { return x + y;};
用接口定义函数的形状interface SearchFunc { (source: string, subString: string): boolean;}let mySearch: SearchFunc;mySearch = function(source: string, subString: string) { return source.search(subString) !== -1;}
可选参数(我们用 ? 表示可选的参数,可选参数后面不允许再出现必需参数了)function buildName(firstName: string, lastName?: string) { if (lastName) { return firstName + ' ' + lastName; } else { return firstName; }}let tomcat = buildName('Tom', 'Cat');let tom = buildName('Tom');
参数默认值(TypeScript 会将添加了默认值的参数识别为可选参数,此时就不受「可选参数必须接在必需参数后面」的限制了)function buildName(firstName: string = 'Tom', lastName: string) { return firstName + ' ' + lastName;}let tomcat = buildName('Tom', 'Cat');let cat = buildName(undefined, 'Cat');
剩余参数(可以使用 ...rest 的方式获取函数中的剩余参数,rest 参数只能是最后一个参数)function push(array: any[], ...items: any[]) { items.forEach(function(item) { array.push(item); });}let a = [];push(a, 1, 2, 3);
重载(允许一个函数接受不同数量或类型的参数时,作出不同的处理)联合类型。。。(然而这样有一个缺点,就是不能够精确的表达,输入为数字的时候,输出也应该为数字,输入为字符串的时候,输出也应该为字符串)function reverse(x: number): number;function reverse(x: string): string;function reverse(x: number | string): number | string | void { if (typeof x === 'number') { return Number(x.toString().split('').reverse().join('')); } else if (typeof x === 'string') { return x.split('').reverse().join(''); }}我们重复定义了多次函数 reverse,前几次都是函数定义,最后一次是函数实现。TypeScript 会优先从最前面的函数定义开始匹配,所以多个函数定义如果有包含关系,需要优先把精确的定义写在前面。
类型断言
类型断言的语法 ----- 值 as 类型 ( tsx 语法中必须使用这个,建议统一使用这个) 或者 <类型>值
类型断言的用途
将一个联合类型断言为其中一个类型interface Cat { name: string; run(): void;}interface Fish { name: string; swim(): void;}/*function isFish(animal: Cat | Fish) { if (typeof (animal as Fish).swim === 'function') { return true; } return false;}*/需要注意的是,类型断言只能够「欺骗」TypeScript 编译器,无法避免运行时的错误,反而滥用类型断言可能会导致运行时错误:function swim(animal: Cat | Fish) { (animal as Fish).swim();}const tom: Cat = { name: 'Tom', run() { console.log('run') }};swim(tom);以上编译时不会报错,但在运行时会报错
将一个父类断言为更加具体的子类interface ApiError extends Error { code: number;}interface HttpError extends Error { statusCode: number;}function isApiError(error: Error) { if (typeof (error as ApiError).code === 'number') { return true; } return false;}
将任何一个类型断言为 any(window as any).foo = 1;(将一个变量断言为 any 可以说是解决 TypeScript 中类型问题的最后一个手段)它极有可能掩盖了真正的类型错误,所以如果不是非常确定,就不要使用 as any。
将 any 断言为一个具体的类型function getCacheData(key: string): any { return (window as any).cache[key];}interface Cat { name: string; run(): void;}const tom = getCacheData('tom') as Cat;tom.run();历史问题,getCacheData返回值是 any,我们在使用它时,最好能够将调用了它之后的返回值断言成一个精确的类型,后续对 tom 的访问时就有了代码补全,提高了代码的可维护性
类型断言的限制
要使得 A 能够被断言为 B,只需要 A 兼容 B 或 B 兼容 A 即可有点复杂,参考: https://ts.xcatliu.com/basics/type-assertion.html
双重断言: todo
任何类型都可以被断言为 anyany 可以被断言为任何类型
interface Cat { run(): void;}interface Fish { swim(): void;}function testCat(cat: Cat) { return (cat as any as Fish);}若你使用了这种双重断言,那么十有八九是非常错误的,它很可能会导致运行时错误。除非迫不得已,千万别用双重断言有点复杂,参考: https://ts.xcatliu.com/basics/type-assertion.html
类型断言 vs 类型转换类型断言只会影响 TypeScript 编译时的类型,类型断言语句在编译结果中会被删除类型断言不是类型转换,它不会真的影响到变量的类型。
类型断言 vs 类型声明 : todo不能将父类的实例赋值给类型为子类的变量。类型声明是比类型断言更加严格的。有点复杂,参考: https://ts.xcatliu.com/basics/type-assertion.html
类型断言 vs 泛型 : todo有点复杂,参考: https://ts.xcatliu.com/basics/type-assertion.html
声明文件
新语法
declare var 声明全局变量declare function 声明全局方法declare class 声明全局类declare enum 声明全局枚举类型declare namespace 声明(含有子属性的)全局对象interface 和 type 声明全局类型export 导出变量export namespace 导出(含有子属性的)对象export default ES6 默认导出export = commonjs 导出模块export as namespace UMD 库声明全局变量declare global 扩展全局变量declare module 扩展模块/// <reference /> 三斜线指令
什么是声明语句declare var jQuery: (selector: string) => any;jQuery('#foo');
什么是声明文件(把声明语句放到一个单独的文件(jQuery.d.ts)中)声明文件必需以 .d.ts 为后缀
第三方声明文件(更推荐的是使用 @types 统一管理第三方库的声明文件)@types 的使用方式很简单,直接用 npm 安装对应的声明模块即可,以 jQuery 举例:npm install @types/jquery --save-dev可以在 https://www.typescriptlang.org/dt/search?search= 搜索你需要的声明文件
书写声明文件 : todo (没看呢,todo)参考: https://ts.xcatliu.com/basics/declaration-files.html
库的使用场景主要有以下几种
全局变量:通过 <script> 标签引入第三方库,注入全局变量
npm 包:通过 import foo from 'foo' 导入,符合 ES6 模块规范
UMD 库:既可以通过 <script> 标签引入,又可以通过 import 导入
直接扩展全局变量:通过 <script> 标签引入后,改变一个全局变量的结构
在 npm 包或 UMD 库中扩展全局变量:引用 npm 包或 UMD 库后,改变一个全局变量的结构
模块插件:通过 <script> 或 import 导入后,改变另一个模块的结构
内置对象
ECMAScript 的内置对象
Boolean、Error、Date、RegExp 等更多内置对象参考: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects定义文件参考: https://github.com/Microsoft/TypeScript/tree/main/src/lib
DOM 和 BOM 的内置对象
Document、HTMLElement、Event、NodeList 等定义文件参考: https://github.com/Microsoft/TypeScript/tree/main/src/lib
TypeScript 核心库的定义文件参考: https://github.com/Microsoft/TypeScript/tree/main/src/lib
进阶
类型别名
我们使用 type 创建类型别名类型别名常用于联合类型
type Name = string;type NameResolver = () => string;type NameOrResolver = Name | NameResolver;function getName(n: NameOrResolver): Name { if (typeof n === 'string') { return n; } else { return n(); }}
字符串字面量类型
我们使用 type 定了一个字符串字面量类型 EventNames,它只能取三种字符串中的一种。注意,类型别名与字符串字面量类型都是使用 type 进行定义。
type EventNames = 'click' | 'scroll' | 'mousemove';function handleEvent(ele: Element, event: EventNames) { // do something}handleEvent(document.getElementById('hello'), 'scroll'); // 没问题handleEvent(document.getElementById('world'), 'dblclick'); // 报错,event 不能为 'dblclick'
元组
数组合并了相同类型的对象,而元组(Tuple)合并了不同类型的对象。
let tom: [string, number] = ['Tom', 25]
但是当直接对元组类型的变量进行初始化或者赋值的时候,需要提供所有元组类型中指定的项。let tom: [string, number];tom = ['Tom'];// Property '1' is missing in type '[string]' but required in type '[string, number]'.
越界的元素当添加越界的元素时,它的类型会被限制为元组中每个类型的联合类型let tom: [string, number];tom = ['Tom', 25];tom.push('male');tom.push(true);// Argument of type 'true' is not assignable to parameter of type 'string | number'.
枚举
简单的例子
enum Days {Sun, Mon, Tue, Wed, Thu, Fri, Sat};
枚举成员会被赋值为从 0 开始递增的数字,同时也会对枚举值到枚举名进行反向映射:console.log(Days["Sun"] === 0); // trueconsole.log(Days[0] === "Sun"); // true
手动赋值
enum Days {Sun = 7, Mon = 6, Tue, Wed, Thu, Fri, Sat};
未手动赋值的枚举项会接着上一个枚举项递增。console.log(Days["Sun"] === 7); // trueconsole.log(Days["Mon"] === 6); // trueconsole.log(Days["Tue"] === 7); // trueconsole.log(Days["Sat"] === 11); // true
常数项和计算所得项
枚举项有两种类型:常数项(constant member)和计算所得项(computed member)
enum Color {Red, Green, Blue = "blue".length};上面的例子中,"blue".length 就是一个计算所得项。
上面的例子不会报错,但是如果紧接在计算所得项后面的是未手动赋值的项,那么它就会因为无法获得初始值而报错:enum Color {Red = "red".length, Green, Blue};// index.ts(1,33): error TS1061: Enum member must have initializer.// index.ts(1,40): error TS1061: Enum member must have initializer.
常数枚举
常数枚举是使用 const enum 定义的枚举类型常数枚举与普通枚举的区别是,它会在编译阶段被删除,并且不能包含计算成员。
const enum Directions { Up, Down, Left, Right}let directions = [Directions.Up, Directions.Down, Directions.Left, Directions.Right];上例的编译结果是:var directions = [0 /* Up */, 1 /* Down */, 2 /* Left */, 3 /* Right */];
假如包含了计算成员,则会在编译阶段报错:const enum Color {Red, Green, Blue = "blue".length};// index.ts(1,38): error TS2474: In 'const' enum declarations member initializer must be constant expression.
外部枚举
外部枚举(Ambient Enums)是使用 declare enum 定义的枚举类型:declare 定义的类型只会用于编译时的检查,编译结果中会被删除。
declare enum Directions { Up, Down, Left, Right}let directions = [Directions.Up, Directions.Down, Directions.Left, Directions.Right];上例的编译结果是:var directions = [Directions.Up, Directions.Down, Directions.Left, Directions.Right];
同时使用 declare 和 const 也是可以的:
类
类的概念
类(Class):定义了一件事物的抽象特点,包含它的属性和方法对象(Object):类的实例,通过 new 生成面向对象(OOP)的三大特性:封装、继承、多态封装(Encapsulation):将对数据的操作细节隐藏起来,只暴露对外的接口。外界调用端不需要(也不可能)知道细节,就能通过对外提供的接口来访问该对象,同时也保证了外界无法任意更改对象内部的数据继承(Inheritance):子类继承父类,子类除了拥有父类的所有特性外,还有一些更具体的特性多态(Polymorphism):由继承而产生了相关的不同的类,对同一个方法可以有不同的响应。比如 Cat 和 Dog 都继承自 Animal,但是分别实现了自己的 eat 方法。此时针对某一个实例,我们无需了解它是 Cat 还是 Dog,就可以直接调用 eat 方法,程序会自动判断出来应该如何执行 eat存取器(getter & setter):用以改变属性的读取和赋值行为修饰符(Modifiers):修饰符是一些关键字,用于限定成员或类型的性质。比如 public 表示公有属性或方法抽象类(Abstract Class):抽象类是供其他类继承的基类,抽象类不允许被实例化。抽象类中的抽象方法必须在子类中被实现接口(Interfaces):不同类之间公有的属性或方法,可以抽象成一个接口。接口可以被类实现(implements)。一个类只能继承自另一个类,但是可以实现多个接口
ES6中类的用法
属性和方法
类的继承
存取器
静态方法
ES7中类的方法
TypeScript 中类的用法
访问修饰符
public
private
protected
参考文档: https://ts.xcatliu.com/basics/built-in-objects.html
0 条评论
下一页