class Button { buttonName: string; } class SpriteSheet { spriteSheetName: string; } class MovieClip { delay: number; } export class TypeExample { constructor() { } execute() { } /** * 数组与元组 * 元组 typescript 1.3 */ executeArrayAndTuple() { // let numberDatas: number[] = [1, 2, 3]; let numberDatas: Array = [1, 2, 3]; //mix type array let mixTypeArray: (number | string | boolean)[] = ['1', 1, true]; // let errorData: [string, number] = [1, 2, 3]; let correctData: [string, number] = ['1', 2]; } /** * typescript 1.4 * 普通字符串与模板字符串(ES6) * ES6标准 */ executeTemplateString() { //普通字符串 let commonStr = "export class TypeExample {\n"; commonStr += "\tconstructor() {\n"; commonStr += "\n"; commonStr += "\t}\n"; commonStr += "\n"; commonStr += "\texecute() {\n"; commonStr += "\n"; commonStr += "\t}\n" commonStr += "}"; console.log(commonStr); console.log('-----------------------------------------'); //模板字符串 let className = `TypeScript`; let templateStr = `export class ${className} { constructor() { } execute() { } }`; console.log(templateStr); } /** * 枚举 */ executeEnum() { //普通数字 enum Color { Red, Blue, Green }; //可以存储字符串,我们通常可以用这个定义常 enum Color2 { Red = "Red", Blue = "Blue", Green = "Green" }; } /** * 类型别名 */ executeAliases() { let data: number | string | boolean | number[] | string[] | boolean[]; let getValue = (data: number | string | boolean | number[] | string[] | boolean[]) => { return data; }; //如果有多处地方用到同样混合类型的,可以通过type将这些混合类型定义一某个特定类型,如: type ComplexType = number | string | boolean | number[] | string[] | boolean[]; let _data: ComplexType; let _getValue = (data: ComplexType) => { return data; } } /** * * 联合类型、交叉类型、类型保护 */ executeUnionAndIntersectionTypes() { /** ------------------联合类型 typescript 1.4 ------------------- */ class CommandClass { name: string; } interface CommandOptions { commandline: string | string[] | Function | CommandClass; } let commandOptions: CommandOptions; //使用类型保护可以获取某种特定类型的类型 if (typeof commandOptions.commandline === "string") { //typeof string let stringCommandLine = commandOptions.commandline; } else if (typeof commandOptions.commandline === "function") { let functionCommandLine = commandOptions.commandline; } else if (commandOptions.commandline instanceof CommandClass) { let classCommandLine = commandOptions.commandline; } else { let arrayCommandLine = commandOptions.commandline; } /** ------------------交叉类型 typescript 1.6------------------- */ //交叉类型可以把多个类型结合在一起,这样就提供了多个类型的特性,如下: interface Birds { fly(): void; } interface Man { walk(): void; } //鸟人 let birdMan: Birds & Man; birdMan.fly(); birdMan.walk(); //合并两个对象的属性值 function mergePropertys(first: T, second: U): T & U { let result = {}; for (let id in first) { (result)[id] = (first)[id]; } for (let id in second) { if (!result.hasOwnProperty(id)) { (result)[id] = (second)[id]; } } return result; } class BirdClass implements Birds { fly(): void { } } class ManClass implements Man { walk(): void { } } let birdMan2 = mergePropertys(new BirdClass(), new ManClass()); birdMan2.fly(); birdMan2.walk(); //交叉类型其它例子 type LinkedList = T & { next: LinkedList }; interface Person { name: string; } let people: LinkedList; let s = people.name; s = people.next.name; s = people.next.next.name; s = people.next.next.next.name; //------------------联合类型与收窄函数--------------------// // 联合类型多用于某些类型可能存在多种可能性,而又需要根据不同的可能性处理不同的结果 // 配合收窄函数可以进行某些特殊的类型判断 interface Dog { run(): void; } interface Pig { sleep(): void; } function isDog(animal: Dog | Pig): animal is Dog { return (animal).run !== undefined; } function runAction(animal: Dog | Pig): void { if (isDog(animal)) { animal.run(); } else { animal.sleep(); } } } /** * 类型推论、断言、保护 */ executeTypeInferenceAndAssertions() { //类型推论 let value = "this is a value"; let value2 = 100; let value3 = [1, 2, 3, 4, 'hello']; for (let i = 0; i < value3.length; i++) { console.log(value3[i]); } let x = [0, 1, 'as']; //这里必须考虑所有元素的类型,所以取其联合类型 console.log(x[1].toString()); /**-------------------------上下文类型--------------------------- */ window.onmousedown = function (mouseEvent) { console.log(mouseEvent.button); //<- Error }; /**-------------------------类型断言--------------------------- */ class DisplayObject { name: string; uid: number; x: number; y: number; width: number; height: number; scaleX: number; scaleY: number; } class DisplayObjectContainer extends DisplayObject { touchEnabled: boolean; addChild: (child: DisplayObject) => DisplayObject; removeChild: (child: DisplayObject) => DisplayObject; } class Sprite extends DisplayObject { graphics: any; } let balls = [new DisplayObjectContainer(), new Sprite()]; //类型断言 (balls[1] as Sprite).graphics; let ball = balls[1]; //类型保护 if (ball instanceof Sprite) { console.log(ball.graphics); } //类型保护 let sprites = [new Button(), new SpriteSheet(), new MovieClip()]; sprites.forEach(value => { if (value instanceof Button) { value.buttonName; } else if (value instanceof SpriteSheet) { value.spriteSheetName; } else if (value instanceof MovieClip) { value.delay; } }); //*---------------------------------------可推断出真实的类型---------------------------------*/ interface Square { kind: "square"; size: number; } interface Rectangle { kind: "rectangle"; width: number; height: number; } interface Circle { kind: "circle"; radius: number; } type Shape = Square | Rectangle | Circle; function area(s: Shape) { // 下面的 switch 语句中,每个 case 子句都限制了 s 的类型。 // 根据对标识属性值的判断,这使得既然不申明类型也可以根据推断出来的类型访问其它属性。 switch (s.kind) { case "square": return s.size * s.size; case "rectangle": return s.width * s.height; case "circle": return Math.PI * s.radius * s.radius; } } /** ----------------------------typescript 2.7------------------------------------------ */ //更精确的类型推断,切换到typescript 2.6.2会报错 let bar = Math.random() < 0.5 ? { a: true, aData: 100 } : { b: true, bData: "hello" }; // if (bar.b) { // bar.bData.toLowerCase(); // } else { // bar.aData.toFixed(2); // } } /** * 幂运算符 */ executeExponentiation() { let value = 2 ** 3; } /** 字符串字面量类型 */ executeStringLiteralTypes() { /** typescript 1.8 */ interface AnimationOptions { easeing: "ease-in" | "ease-out" | "ease-in-out"; } type Animation = { new(callback: () => void): T }; let _type_: 1 & 2 & 3; } /** * 只读属性 * typescript 2.0 */ executeReadonly() { class Transform { /** readonly typescript 2.0 */ readonly x: number; readonly y: number; } let transform = new Transform(); // transform.x = 1; } /** * this * typescript 2.0 */ executeThis() { class BasicCalculator { public constructor(protected value: number = 0) { } public currentValue(): number { return this.value; } public add(operand: number): this { this.value += operand; return this; } public multiply(operand: number): this { this.value *= operand; return this; } // ... other operations go here ... } let v = new BasicCalculator(2) .multiply(5) .add(1) .currentValue(); //如果有一个类继承自BasicCalculator,并且也需要连接方法,那么,this就比较有作用了,否则,就不能连接起来 class ScientificCalculator extends BasicCalculator { public constructor(value = 0) { super(value); } public sin() { this.value = Math.sin(this.value); return this; } } v = new ScientificCalculator(2) .multiply(5) .sin() .add(1) .currentValue(); } /** * never类型 * typescript 2.0 */ executeNeverType() { // 函数返回never必须无法执行到终点 function error(message: string): never { throw new Error(message); } // 推断返回类型是never function fail() { return error("Something failed"); } // 函数返回never必须无法执行到终点 function infiniteLoop(): never { while (true) { } } function move1(direction: "up" | "down") { switch (direction) { case "up": return 1; case "down": return -1; } return error("Should never get here"); } } /** * typescript 2.1 * 索引访问类型 * */ executeIndexAccessType() { class Rect { x: number; y: number; width: number; height: number; toString: () => void; } type XType = Rect['x']; type ToStringType = Rect['toString']; //在泛型里可以这样: function getProperty(obj: T, key: K) { return obj[key]; // 推断类型是T[K] } } /** * typescript 2.1 * 映射类型 */ executeMapTypes() { //目标:使用现有类型,并使用属性完成可选 interface Person { name: string; age: number; location: string; } // type Partial = { // [K in keyof T]?: T[K]; // } type PartialPerson = Partial; //等价于 // interface PartialPerson { // name?: string; // age?: number; // location?: string; // } //readonly // type Readonly = { // readonly [K in keyof T]?: T[K]; // } type ReadonlyPerson = Readonly; //将每个属性映射成Promise type PromiseValue = {[K in keyof T]: Promise; }; //同样我们可以对任何属性进行映射成任意的,如: type Validator = { callback: (object: T, key: string, componentName: string, ...rest: any[]) => void }['callback']; type ValidatorMap = {[K in keyof T]?: Validator }; class BaseClass { mp: number; hp: number; } let baseClass: ValidatorMap; baseClass.hp(new BaseClass(), '', ''); baseClass.mp(new BaseClass(), '', ''); type PromisePerson = PromiseValue; //proxy type ProxyValue = { [K in keyof T]: { get(): T[K]; set(v: T[K]): void; }; } //在标准库中我们定义了一些常用的映射类型 // Partial 将对象所有的属性变成可选 // Readonly 将对象所有的属性变成可读 // Required 将对象所有的属性变成必选 // Pick 从对象中取可用的属性 // Record // Exclude // Extract // NonNullable // ReturnType // InstanceType //1、Partical作用 interface UserData { /** 玩家正在进行游戏的游戏服务器serverid,非appid */ activeGame: number; /** 由游戏编号与玩家id生成的玩家游戏ID */ gid: number; /** 玩家头像url */ headImgUrl: string; /** 用户最后登录时间 */ lastLoginTime: number; /** 玩家昵称 */ nickName: string; /** 用户中心refreshtoken */ refreshToken: string; /** 用户中心accessToken 非微信端accessToken */ accessToken: string; /** 用户注册时间 */ regTime: number; /** 用户性别 */ sex: number; /** 玩家项目游戏id */ userId: number; /** 用户ip */ userIp: string; /** 用户身份,目前有普通玩家0和GM身份1,新注册玩家999 */ userRole: number; } //惰性附值的时候 class UserDataMrg { private data: Partial = {}; setGid(gid: number) { this.data.gid = gid; } } class BaseObject { hp: number; mp: number; width?: number; height?: number; } type PartialType = Partial; type ReadOnlyType = Readonly; // type RequiredType = Req; type PickType = Pick; type RecordType = Record<"mp", BaseObject>; } /** * typescript 2.2 * 对象类型 * 如果函数参数中只能传对象,不能传比如number、string、boolean等基本类型,那么,typescript2.2中新增的object类型可以处理 * 之前的做法是传any或者,不传类型,这种情况下无法约束为对象类型。 * */ executeObjectType() { let setState = (object: object) => { //todo }; setState({ a: 1 }); // setState(1);//error } /** * symbol * 为了解决冲突而存在 * 为了解决内部变量因为过长的问题而存在 */ executeSymbolType() { class MouseEvent { static MOUSE_DOWN: string = "mousedown"; static MOUSE_UP: string = "mousedup"; static MOUSE_MOVE: string = "mousemove"; } class MouseEvent_2 { static MOUSE_DOWN: string = "mousedown"; static MOUSE_UP: string = "mousedup"; static MOUSE_MOVE: string = "mousemove"; } function dispatchEvent(type: string | Symbol, handler: (...args) => void, thisObj?: any): void { } //这两种情况一样 dispatchEvent(MouseEvent.MOUSE_DOWN, () => { }); dispatchEvent(MouseEvent_2.MOUSE_DOWN, () => { }); //通过下面的方法可以避免重复 class MouseEvent_3 { static MOUSE_DOWN: Symbol = Symbol("mousedown"); static MOUSE_UP: Symbol = Symbol("mousedup"); static MOUSE_MOVE: Symbol = Symbol("mousemove"); } class MouseEvent_4 { static MOUSE_DOWN: Symbol = Symbol("mousedown"); static MOUSE_UP: Symbol = Symbol("mousedup"); static MOUSE_MOVE: Symbol = Symbol("mousemove"); } //这两种情况不一样 dispatchEvent(MouseEvent_3.MOUSE_DOWN, () => { }); dispatchEvent(MouseEvent_4.MOUSE_DOWN, () => { }); //原来的处理方式 class GameObject { private ________$direction_______: string = 'left'; set direction(value: string) { this.________$direction_______ = value; } run(): void { switch (this.________$direction_______) { case "left": break; case "right": break; case "top": break; case "bottom": break; } } } //可动态的改变类实例值 let g = new GameObject(); g['________$direction_______:'] = "top"; g.run(); //改进后 class GameObject2 { private _direction: Symbol = Symbol("left"); set direction(value: string) { if (!Symbol.for(value)) { this._direction = Symbol(value); } } run(): void { switch (this._direction) { case Symbol.for("left"): break; case Symbol.for("right"): break; case Symbol.for("top"): break; case Symbol.for("bottom"): break; } } } let g2 = new GameObject2(); //如果直接来修改是不会改变原来的值的 // g2['_direction'] = Symbol('right'); // 只有通过下面的方式来修改才可以 g2.direction = "right"; g2.run(); } }