TypeScript知识点
定义
TypeScript 是由微软开发的自由和开源的编程语言, 是JavaScript 的一个超集,支持 ECMAScript 6 标准。其设计目标是开发大型应用,它可以编译成纯 JavaScript,编译出来的 JavaScript 可以运行在任何浏览器上。
复杂的类型声明示例
- https://github.com/piotrwitek/utility-types
- https://github.com/millsp/ts-toolbelt
- https://github.com/andnp/SimplyTyped
- ts内置示例
常用知识点
-
修饰符
- public 在TypeScript里,成员都默认为 public,我们可以自由的访问程序里定义的成员
- private 当成员被标记成 private时,它就不能在声明它的类的外部访问
- protected 和private类似, 但是protected成员在派生类中仍然可以访问
-
常用类型
string
、boolean
、number
、number[]
、、、、 -
type和interface区别
- 相同点
- 类型:对象、函数两者都适用
- 都能通过extends进行扩展
- 不同点
- type能够声明别名而interface不行,type 可以用于基础类型、联合类型、元组。
- 同名合并:interface 支持,type 不支持。
interface User {name: stringage: number}interface User {sex: string}/*User 接口为 {name: stringage: numbersex: string}*/- 计算属性:type 支持, interface 不支持。
let div = document.createElement('div');type B = typeof div
- 相同点
-
用法概览1
// 布尔类型let isCookie:boolean = true
// 数值类型let myMoney:number = 12
// 字符串类型let name:string = '徐小夕'
// 数组类型, 有两种表示方式,第一种可以在元素类型后面接上[],表示由此类型元素组成的一个数组let arr:number[] = [1,2,2]
// 数组类型, 使用数组泛型let arr:Array<number> = [1,2,2]
// 元组类型, 允许表示一个已知元素数量和类型的数组,各元素的类型不必相同let xi: [string, number];// 初始化xixi = ['xu', 10]; // 正确xi = [11, 'xu']; // 错误
// 枚举类型, 可以为一组数值赋予友好的名字enum ActionType { doing, done, fail }let action:ActionType = ActionType.done // 1
// any, 表示任意类型, 可以绕过类型检查器对这些值进行检查let color:any = 1color = 'red'
// void类型, 当一个函数没有返回值时,通常会设置其返回值类型是 voidfunction getName(): void { console.log("This is my name");}
// object类型, 表示非原始类型,也就是除number,string,boolean,symbol,null或undefined之外的类型let a:object;a = {props: 1}
- 用法概览2
// 类型断言- as or <>
// interface用于对象interface App { name: string; color?: number; [propName: string]: any;}
// interface用于函数interface MyFunc { (value:string, type: number): boolean;}
// interface用于类interface TickInterface { tick();}class DigitalClock implements TickInterface { constructor(h: number, m: number) { } tick() { console.log("xu xu"); }}
// 抽象类做为其它派生类的基类使用。它们一般不会直接被实例化。不同于接口,抽象类可以包含成员的实现细节。abstract关键字是用于定义抽象类和在抽象类内部定义抽象方法。abstract class MyAbstract { constructor(public name: string) {} say(): void { console.log('say name: ' + this.name); } abstract sayBye(): void; // 必须在派生类中实现}class AccountingMyAbstract extends MyAbstract { constructor() { super('test'); // 在派生类的构造函数中必须调用 super() } sayBye(): void { console.log('test1'); } getOther(): void { console.log('test2'); }}let department: MyAbstract; // 允许创建一个对抽象类型的引用department = new MyAbstract(); // 错误: 不能创建一个抽象类的实例department = new AccountingMyAbstract(); // 允许对一个抽象子类进行实例化和赋值department.say();department.sayBye();department.getOther(); // 错误: 方法在声明的抽象类中不存在
// 函数,在参数名旁使用 ?实现可选参数的功能,可选参数必须跟在必选参数后面function createName(firstName: string, lastName?: string) { if (lastName) return firstName + " " + lastName; else return firstName;}
/** 泛型 */// 泛型-函数中直接使用function iSay<T>(arg: T): T { // 整体使用泛型 return arg;}let come = iSay<number>(123);
function iSay<T>(arg: T[]): T[] { // 局部使用泛型 console.log(arg.length) return arg;}// 泛型-函数中定义使用interface SayLove { <T>(arg: T): T}interface SayLoveArg<T> { // 把泛型参数当作整个接口的一个参数 (arg: T): T}let mySay1:SayLove = iSaylet mySay2:SayLoveArg<number> = iSay
// 泛型-类中直接使用class MyNumber<T> { year: T; compute: (x: T, y: T) => T;}let myGenericNumber = new MyNumber<number>();
// 泛型-类中定义使用-类似类型别名interface NumberControl { length: number}class MyObject<T extends NumberControl>(arg: T):T { console.log(arg.length) return arg}
// 交叉类型function extend<T, U>(first: T, second: U): T & U { let result = {} as T & U; // <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;}
// 联合类型let name: string | number = 'test';
// 多态的 this类型class MyCalculator { public constructor(number = 0) { } public add(n: number): this { this.value += n; return this; } public multiply(n: number): this { this.value *= n; return this; } // ... 其他操作 ...}
// 索引类型查询操作符interface Animal { cat: string; dog: string;}let AnimalProps: keyof Animal; // 'cat' | 'dog'
// 索引访问操作符interface TestInterface { a: string, b: number, c: boolean, d: symbol, e: null, f: undefined, g: never}type MyType = TestInterface[keyof TestInterface]; // 通过[]索引类型访问操作符, 我们就能得到某个索引的类型
// 命名空间-主要作用是用来组织代码,以便于在记录它们类型的同时还不用担心与其它对象产生命名冲突。declare namespace D3 { export interface Selectors { select: { (selector: string): Selection; (element: EventTarget): Selection; }; } export interface Event { x: number; y: number; } export interface Base extends Selectors { event: Event; }}declare var d3: D3.Base;
// 使用第三方类库npm install --save lodash @types/lodashimport * as _ from "lodash"; // 在代码中使用_.padStart("Hello world!", 2, " ");
// 声明文件-使用未经声明的全局函数或者变量, typescript会报错, 可以添加xxx.d.ts文件, 并在里面声明所需变量, ts会自动检索到该文件并进行解析。// global.d.tsdeclare var name: string;// 1. 声明全局变量declare function say(name: string): void;// 2. 全局函数declare namespace myObj {// 3. 带属性的对象 function say(s: string): string; let money: number;}interface Animal {// 4. 可重用的接口 kind: string; age: number; weight?: number;}declare function findAnmiate(animal:Animal):void;
常见DEMO
/** 一、interface和type有什么区别 */// ****相同点****// 1. 都能声明对象// 2. 都能通过extends进行扩展
// 通过interface声明对象结构interface A { a: string;}// interface扩展另一个interfaceinterface AA extends A { b: number;}// 通过type声明对象结构type B = { a: string;};// type扩展另一个typetype BB = B & { b: number };// type和interface还能相互继承type B = { a: string;};interface C extends B { b: number;}// ****不同点****// 1. type能够声明别名而interface不行// 2. 同名合并:interface 支持,type 不支持。// 3. 计算属性,type 支持, interface 不支持。type isNumberOrBool = number | boolean;let a: isNumberOrBool = 1;a = true;// Bar { a, b, c }interface Bar { a: string b: number } interface Bar { c: string }type Keys = "firstname" | "surname";type DudeType = { [key in Keys]: string}
/** 二、函数参数是对象时,会对对象字面量做额外的属性检查,但却不会对对象引用做检查处理 */interface O { a: string; b: number;}function fn(o: O) {}const o = { a: "a", b: 1, c: 3 };// 下面一行能通过ts检查fn(o);// 下面一行不通过, 提示'c' does not exist in type 'O'fn({ a: "a", b: 1, c: 3 });
/** 三、TypeScript的类型兼容性是基于结构的而非名义的 */interface Named { name: string;}class Person { name: string;}let p: Named;// OK, because of structural typingp = new Person();
/** 四、模块,namespace和declare声明文件三者导入导出的区别 */// 模块通过import/export去导入// namespace和declare声明则是通过三斜线指令/// <reference path="..." />的方式去导入的
/** 五、传递过程中导致this丢失,指向全局,但是并没有收到报错 */const fn = o.do;// 报错,提示:The 'this' context of type 'void' is not assignable to method's 'this' of type 'O'fn();
常见问题
Q: vscode中直接运行typescript需要安装A: npm install -D tslib @types/nodenpm install -g typescript ts-node