1.0.8 • Published 6 years ago
typescript-example-learn v1.0.8
vscode 相关
- 时间
- 1999/11: ES 3 发布;
- 2008/08: ES 4 被废弃;(v8引擎发布)
- 2009/11: ES 3.1 改名 ES 5 发布;(nodejs发布)
- 2010 (npm发布)
- 2011/06: ES 5.1 发布;
- 2012/10: TypeScript 0.8 发布;
- 2015/06: ES 2015 发布;(ES6、vscode)
- npm
npm init npm install watch --save npm install watch --save-dev npm install typescript -g npm install @types/react --save-dev npm uninstall watch --save npm adduser npm publish(每次修改后,需要更新版本号) npm update npm ls npm run xxx
cnpm
通常有些第三方库通过npm命令很难安装,这时候可以先安装cnpm,再借助cnpm来安装一些需要的库:npm install -g cnpm --registry=https://registry.npm.taobao.org 安装完毕之后,通过如下命令: cnpm install electron -g
- name 如果我们准备将我们的包发布到npm上,那么,name字段是必须的,且name字段不能与npm上的同名
- private
默认为
true
,那么通过npm publish
,可以将此库发布到npm官网,否则,将作为私有库 scripts 可以运行配置好的脚本,如:
scripts { "compile": "webpack --config webpack.config.js --progress --colors --watch", "dev": "electorn .", "build": "webpack --config webpack.config.js --progress --colors" }
上面配置的脚本可能过以下命令来执行:
npm run compile npm run dev npm run build
注意,每个脚本都能够在一定时间内执行完,并退出,否则会中止在某个脚本上
- 原理
每当执行
npm run
就会自动构建一个bash
,而需要注意的是npm run
新建的bash
会将当前目录的node_modules/.bin
子目录加入到PATH
变量,执行结束后,再将PATH
恢复原样,这就意味着我们不需要加入路径就可以执行命令 - 通配符
"lint": "jshint *.js" "lint": "jshint **/*.js"
- 传参
向 npm 脚本传入参数,要使用 -- 标明,如:npm run lint -- hello
- 执行顺序
- 并行执行,通过
&
来分隔,如:npm run script1 & npm run script2
- 顺序执行,通过
&&
来分隔,如:npm run build && npm run dev
- 并行执行,通过
- 原理
每当执行
dependencies 业务依懒,只应用于生产环境,发布后所需要的包,使用
--save
安装- devDependencies
开发环境依懒,只应用于开发环境,发布后不需要的包,使用
--save-dev
安装 - main
入口文件,通过
require('package#name')
可以引入该库
{ "compilerOptions": { "target": "es5", "outDir": "bin-debug", "sourceMap": true, "experimentalDecorators": true, "lib": [ "es5", "dom", "es6", "es2015.promise" ], "types": [] }, "include": [ "src", "libs" ], "exclude": [ "net" ] }
lib可填
dom webworker es5 es6 / es2015 es2015.core es2015.collection es2015.iterable es2015.promise es2015.proxy es2015.reflect es2015.generator es2015.symbol es2015.symbol.wellknown es2016 es2016.array.include es2017 es2017.object es2017.sharedmemory scripthost
- typescript.tsdk
使用新的typescript版本替换vscode自带的typescript
"typescript.tsdk": "node_modules\\typescript\\lib"
- files.exclude
排除vscode 左侧树显示的文件
"files.exclude": { "**/.git": true, "**/.svn": true, "**/.hg": true, "**/CVS": true, "**/.DS_Store": true }
- editor.formatOnSave
保存文件的时候对ts文件进行格式化
"editor.formatOnSave": true
- typescript.tsdk
使用新的typescript版本替换vscode自带的typescript
调试 vscode是基于node环境并能够调试 javascript、typescript及其它语言。其它语言一般情况下需要安装相关语言插件,例如Php、Python、C++、Java等,调试设置通过lanuch.json来设置
- request
(attach、launch)
- type
node、chrome
- name
- 常用例子
{ "version": "0.2.0", "configurations": [ { "type": "node", "request": "launch", "name": "Launch Program", "program": "${workspaceFolder}/out/Main.js" } ] } { "version": "0.2.0", "configurations": [ { "type": "node", "request": "attach", "name": "Attach Program", "program": "${workspaceFolder}/out/Main.js" } ] }
- request
markdown 通常我们写教程用markdown十分方面,此方档就是用markdown写的
ES6与Typescript
常用特性
- 模块与命名空间
- 模块解析
{ "compilerOptions": { "baseUrl": "./src", "paths": { "*": [ "*", "../extensions/*" ] } } }
- 相对与非相对,通过在tsconfig.json件中的baseUrl属性可以修改
- let
for (let i = 0; i < 10; i++) { setTimeout(() => { console.log(i); }, 2000); }
- 接口
interface Animal { weight: number; }
类(静态部分、实例部分)
//类静态部分与实例部分,先定义静态部分的接口 interface ClockInterface { tick(); } interface ClockConstructor { new(hour: number, minute: number): ClockInterface; show(): void; } let Clock: ClockConstructor; let clock = new Clock(1, 1); Clock.show();
接口与类区别使用
//当很多时候我们需要毫无关系的对象集提供统一的属性或者方法调用,此用用接口更好,例如: interface WeightInfo { weight: number; } interface ColorInfo { color: number; } class Man implements WeightInfo { weight: number; } class Car implements WeightInfo, ColorInfo { weight: number; color: number; } class Wall implements ColorInfo { color: number; } function choose(object: WeightInfo | ColorInfo) { }
函数(箭头、构造函数、重载)
//箭头函数 let add = (a: number, b: number) => { return a + b; }; //构造函数 class Move{ constructor(public speed: number, private x: number, protected y: number) { } start(){ this.speed; this.x; this.y; } } //重载 function pickCard(x: {suit: string; card: number; }[]): number; function pickCard(x: number): {suit: string; card: number; }; function pickCard(x): any { if (typeof x == "object") { //todo }else if(typeof x == "number"){ //todo } }
object类型
let setState = (object: object) => { //todo }; setState({ a: 1 }); // setState(1);//error
枚举
//普通数字 enum Color { Red, Blue, Green }; //可以存储字符串,我们通常可以用这个定义常 enum Color2 { Red = "Red", Blue = "Blue", Green = "Green" };
类型别名
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; }
数组与元组
// let numberDatas: number[] = [1, 2, 3]; let numberDatas: Array<number> = [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];
索引访问类型
class Rect { x: number; y: number; width: number; height: number; toString: () => void; } type XType = Rect['x']; type ToStringType = Rect['toString']; //在泛型里可以这样: function getProperty<T, K extends keyof T>(obj: T, key: K) { return obj[key]; // 推断类型是T[K] }
普通字符串与模板字符串
//普通字符串 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);
泛型
/** * 泛型最大的作用: * (1):类型提示 * (2):类型约束 * */ //泛型可以附初始值 class Component<P = {}, S = {}> { props: P; constructor(props?: P) { this.props = props; } }
class Ball<P, S> extends Component<P, S> {
}
class GameObject<T> {
getElemntByQualifiedName<T>(value: { new(): T }): T {
return new value();
}
}
interface LengthWise {
length: number;
}
interface Person {
name: string;
age: number;
}
interface EventType {
click: { type: string, bubbles: boolean }
mousedown: { type: string, bubbles: boolean };
mouseup: { type: string };
}
interface EventTyp3<T> {
addEventListener(type: T, handler: () => void): void;
addEventListener(type: string, handler: () => void): void;
}
class BeeKeeper {
hasMask: boolean;
}
class ZooKeeper {
nametag: string;
}
class Animal {
numLegs: number;
}
class Bee extends Animal {
keeper: BeeKeeper;
}
class Lion extends Animal {
keeper: ZooKeeper;
}
export class GenericsExample {
execute() {
let component = new Component({ a: 1, b: 2 });
let gameobject = new GameObject<Component<1, 2>>();
let comp = gameobject.getElemntByQualifiedName(Component);
//类型推断
let values = [1, 2, 3, 4, 5];
let finds = values.find((value, index, arr) => {
return value > 3;
});
console.log(finds);
//泛型约束 typescript 1.8
//希望传入的数据有个length属性
this.genericsConstraint("1");//ok
// this.genericsConstraint(1);//error
this.genericsConstraint({ length: 100, width: 100 });//ok
//约束key是对象obj的属性
let states = {
width: 100,
height: 100
};
//为了保证键值在有限范围内,需要通过 extends keyof 来处理
let propertyValue = this.getPropertyValue(states, "width");
console.log(propertyValue);
type personKey = keyof Person;
let key: personKey;
switch (key) {
case 'name':
break;
case "age":
break;
// case "location": //error
// break;
}
this.addEventListener("mouseup", (evt) => {
console.log(evt)
}, this);
//assign
console.log(this.assign({ a: 1, b: 2, c: 3 }, { a: 10, c: 20 }));
//-------------------泛型参数默认类型--TODO-------------------//
class GameObject1<T extends Component = Component>{
}
//因为有默认类型,这里不需要传入泛型类
let gameobject1 = new GameObject1();
// class Component2<P, S> {
// props: P;
// constructor(props?: P) {
// this.props = props;
// }
// }
// class GameOBject2<T extends Component2>{
// }
// let gameobject2 = new GameOBject2();
interface Person {
name: string;
age: number;
location: string;
}
type P = keyof Person;
let p: P;
//等价于下面
let p1: "name" | "age" | "location";
type P2 = keyof Person[];
/** keyof 处理类型继承,类似交叉类型 */
class Sub {
a: number;
b: number;
}
class Parent extends Sub {
c: number;
}
type b = keyof Parent;
/**-----------------------泛型类型---------------------- */
function identity<T>(arg: T): T {
return arg;
}
let myIdentity: <T>(arg: T) => T = identity;
let myIdentity2: { <T>(arg: T): T } = identity;
interface GenericIdentityFn {
<T>(arg: T): T;
}
let myIdentify3: GenericIdentityFn = identity;
myIdentify3(10);
interface GenericIdentityFn2<T> {
(arg: T): T;
}
let myIdentify4: GenericIdentityFn2<string> = identity;
myIdentify4('10');
this.create(Parent);
this.create2(Parent);
this.createInstance(Bee);
this.createInstance(Animal);
// this.createInstance(BeeKeeper); //error
}
genericsConstraint<T extends LengthWise | { length: number }>(value: T): T {
return value;
}
/**
* 将源值的部分数据附给target
*/
assign<T extends U, U>(target: T, source: U): T {
for (let id in source) {
(<any>target)[id] = source[id];
}
return target;
}
/** 注册一个可选范围键值 */
addEventListener<K extends keyof EventType>(type: K, listener: (evt?: EventType[K]) => void, thisObject: any): void {
//logic
}
removeEventListener<K extends keyof EventType>(type: K, listener: (evt?: EventType[K]) => void): void {
}
/**
* typescript 2.1
* @param obj
* @param key
*/
getPropertyValue<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
create<T>(o: { new(): T }): T {
return new o();
}
create2<T>(o: new () => T): T {
return new o();
}
createInstance<T extends Animal>(o: new () => T): T {
return new o();
}
}
```
* 解构
```Typescript
/**解构 */
export class SpreadExample {
execute(): void {
//---------------解构数组------------------//
let datas = [1, 2];
let [value1, value2] = datas;
console.log(value1, value2);
//可以很方便的交换数据
let arr1 = [1, 2, 3];
let arr2 = [10, 20, 30];
//传递键值(0,1)解构数组
[arr2, arr1] = [arr1, arr2];
console.log(arr1, arr2);
//解构剩余参数
let [first, ...rest] = [1, 2, 3, 4, 5];
console.log(first);
console.log(rest);
//---------------解构对象------------------//
let states = {
width: 100,
height: 200
};
//传递健值(width,height)解构状态
const { width, height } = states;
console.log(width, height);
//可以重命名解构名字(:后面是类型)
const { width: widthRename, height: heighRename }: { width: number, height: number } = states;
console.log(widthRename, heighRename);
}
}
```
* 联合与交叉类型
```Typescript
/** ------------------联合类型 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<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;
}
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> = T & { next: LinkedList<T> };
interface Person {
name: string;
}
let people: LinkedList<Person>;
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 (<Dog>animal).run !== undefined;
}
function runAction(animal: Dog | Pig): void {
if (isDog(animal)) {
animal.run();
} else {
animal.sleep();
}
}
```
* 类型推论、类型断言、类型保护
```Typescript
//类型推论
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());
//获取所有的
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;
}
});
/**-------------------------上下文类型--------------------------- */
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);
}
//*---------------------------------------可推断出真实的类型---------------------------------*/
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);
// }
```
* Promise
```Typescript
/**
* typescript 1.7 编译到ES6(Node 4+)
*/
export class PromiseExample {
constructor() { }
execute() {
console.log('trace 1');
this.executeDelay();
console.log('trace 2');
}
async executeDelay() {
let delay1 = await this.delay1();
console.log(delay1);
let delay2 = await this.delay2();
console.log(delay2);
let currentTime = Date.now();
// console.log('-------------------------------------', currentTime);
// 使用场景,有时候可能会接收服务器提供的多种数据后才能做别的操作,而这多种结果并不知道先后顺序。这时使用Promise.all比较方便
Promise.all([this.delay1(), this.delay2()]).then((resolve) => {
let _delay1 = resolve[0];
let _delay2 = resolve[1];
console.log(_delay1);
console.log(_delay2);
console.log(Date.now() - currentTime);
});
console.log('end');
//当参数里的任意一个子promise成功或者失败后,才返回
let currentTime2 = Date.now();
// 使用场景,有时候我们需要接收服务器的多种数据都可以操作,但不确定哪个最先收到,通过Promise.race可以更快的处理结果
Promise.race([this.delay1(), this.delay2()]).then((fastReuslt) => {
console.log('***:', fastReuslt);
console.log(Date.now() - currentTime2);
});
//1秒输出一次
let delay = async (i) => {
return new Promise((c, e) => {
setTimeout(() => {
c(i);
}, 1000);
})
}
let trace = async () => {
for (let i = 0; i < 10; i++) {
let out = await delay(i);
console.log(out);
}
}
}
private delay1(): Promise<string> {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve("delay1Complete");
}, 2000);
});
}
private delay2(): Promise<string> {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve("delay2Complete");
}, 1000);
});
}
}
```
* 装饰器
```Typescript
function ClassDecorator(path: string) {
return function (target: Function) {
!target.prototype.$Meta && (target.prototype.$Meta = {})
target.prototype.$Meta.baseUrl = path;
debugger;
};
}
function MethodDecorator(url: string) {
return function (target, methodName: string, descriptor: PropertyDescriptor) {
!target.$Meta && (target.$Meta = {});
target.$Meta[methodName] = url;
debugger;
}
}
function StaticMethodDecorator(url: string) {
return function (target, methodName: string, descriptor: PropertyDescriptor) {
debugger;
}
}
function ParameterDecorator(paramName: string) {
return function (target, methodName: string, paramIndex: number) {
!target.$Meta && (target.$Meta = {});
target.$Meta[paramIndex] = paramName;
console.log('ParameterDecorator:', paramName);
debugger;
}
}
function StaticParameterDecorator(paramName: string) {
return function (target, methodName: string, paramIndex: number) {
console.log('StaticParameterDecorator:', paramName);
debugger;
}
}
function PropertyDecorator(value: string) {
//属性描述符不会做为参数传入属性装饰器
//属性描述符只能用来监视类中是否声明了某个名字的属性。
return function (target: any, propertyName: string) {
target[propertyName] = value;
console.log('PropertyDecorator:', value);
debugger;
}
}
function StaticPropertyDecorator(value: number) {
return function (target: any, propertyName: string) {
console.log('StaticPropertyDecorator:', value);
debugger;
}
}
/**
* typescript 1.5
* 装饰器
* 类、属性、方法、方法参数、getter/setter
* 执行顺序
* 1、
*/
@ClassDecorator('/hello')
export class DecoratorExample {
@PropertyDecorator('hello,world')
greeting: string;
@StaticPropertyDecorator(100)
static hp: number;
@MethodDecorator("http://www.baidu.com")
execute(@ParameterDecorator("userId") userId: string, @ParameterDecorator("userId2") userId2: string) {
}
@StaticMethodDecorator("http://www.baidu.com")
static show(@StaticParameterDecorator("userId") userId: string, @StaticParameterDecorator("userId2") userId2: string) {
}
}
console.log(DecoratorExample.prototype['$Meta']);
console.log(new DecoratorExample().greeting);
```
* 映射类型
```Typescript
//目标:使用现有类型,并使用属性完成可选
interface Person {
name: string;
age: number;
location: string;
}
// type Partial<T> = {
// [K in keyof T]?: T[K];
// }
type PartialPerson = Partial<Person>;
//等价于
// interface PartialPerson {
// name?: string;
// age?: number;
// location?: string;
// }
//readonly
// type Readonly<T> = {
// readonly [K in keyof T]?: T[K];
// }
type ReadonlyPerson = Readonly<Person>;
//将每个属性映射成Promise
type PromiseValue<T> = {[K in keyof T]: Promise<T[K]>; };
//同样我们可以对任何属性进行映射成任意的,如:
type Validator<T> = { callback: (object: T, key: string, componentName: string, ...rest: any[]) => void }['callback'];
type ValidatorMap<T> = {[K in keyof T]?: Validator<T> };
class BaseClass {
mp: number;
hp: number;
}
let baseClass: ValidatorMap<BaseClass>;
baseClass.hp(new BaseClass(), '', '');
baseClass.mp(new BaseClass(), '', '');
type PromisePerson = PromiseValue<Person>;
//proxy
type ProxyValue<T> = {
[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<UserData> = {};
setGid(gid: number) {
this.data.gid = gid;
}
}
class BaseObject {
hp: number;
mp: number;
width?: number;
height?: number;
}
type PartialType = Partial<BaseObject>;
type ReadOnlyType = Readonly<BaseObject>;
// type RequiredType = Req<BaseObject>;
type PickType = Pick<BaseObject, "hp">;
type RecordType = Record<"mp", BaseObject>;
```
* 不可及代码
``` Typescript
getValue(type: "up" | "down"){
switch(type){
case "up":
break;
case "down"
break;
default:
//不可及地方
break;
}
}
```
* 字面量类型
```Typescript
/** typescript 1.8 */
interface AnimationOptions {
easeing: "ease-in" | "ease-out" | "ease-in-out";
}
type Animation<T> = { new(callback: () => void): T };
let _type_: 1 & 2 & 3;
```
* Symbol
为了解决属性名冲突的问题而存在
``` Typescript
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();
```
>必须使用方括号来访问,不能使用点语法访问
* Proxy
``` Typescript
class B {
hp: number = 10;
mp: number = 100;
}
let target = {};
let handler = {
get: (target: any, p: PropertyKey, receiver: any) => {
if (p === "toString") {
return '[TAEGET-toString:]' + target.toString();
} else {
return target[p];
}
},
set: (target: any, p: PropertyKey, value: any, receiver: any) => {
try {
let oldValue = target[p];
if (oldValue !== value) {
console.log(`值从${oldValue},变成:${value}`);
target[p] = value;
}
} catch{
}
return true;
}
};
let _b = new B();
let p = new Proxy(target, handler);
// p.o = new B();
// p.o = 1;
// p.o = 1;
// p.o = true;
// p.o = true;
// p.o = _b;
// p.o = _b;
// console.log(p.toString);
// var object: any = { proxy: new Proxy(target, handler) };
let obj = Object.create(p);
obj.o = new B();
obj.o = 1;
obj.o = 1;
obj.o = true;
obj.o = true;
obj.o = _b;
obj.o = _b;
console.log(obj.toString);
//-----------用于判断拦截某个属性的读取操作-----------------//
let proxy = new Proxy(_b, {
get: function (target, p: PropertyKey) {
if (p in target) {
return target[p];
} else {
throw new ReferenceError(`属性:${p}不存在!!`);
}
}
});
// proxy['height'];
// Proxying a function object
let target1 = function () { return 'I am the target'; };
let handler1 = {
apply: (receiver, ...args) => {
return 'I am the proxy';
}
};
let p1 = new Proxy(target1, handler1);
p1() === 'I am the proxy';
```
其它
- never类型
// 函数返回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"); }
- this类型
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();
- 收窄函数
interface Dog { run(): void; } interface Pig { sleep(): void; } function isDog(animal: Dog | Pig): animal is Dog { return (<Dog>animal).run !== undefined; } function runAction(animal: Dog | Pig): void { if (isDog(animal)) { animal.run(); } else { animal.sleep(); } }
规范
- 如果在有限集里能够确定类型,一定要定义
type
或者interface
,否则定义成any
类型。 - 如果有纯数据,一定要定义它的接口文件,例如,收到服务器的数据
vo
,或者发给服务器的数据vo
。 - 如果
tsc
不能根据字面量类型推导出来,一定得添加自己的类型,以方便自己与别人在后期开发过程中有类型提示。 - 如果需要约束某个键值(通常像一些
switch
的常量,事件的名字等),可通过泛型keyof
或者extends keyof
来实现。 - 如果需要定义一些通用接口,但又不确定传入什么类型,可利用
泛型
来处理。 - 尽量不要用
function
来表示函数,而使用箭头函数来替代,除非你有通过function里的作用域来获取函数实例本身。 - 尽量不要用
var
来申明变量,改为用let来申明变量,一般情况下,实在找不出理由再使用var
关键字的。 - 如果typescript能够推断出类型,尽量不要添加类型,例如:
let a = 1
。 - 如果需要用比较运算符,比如
==
,尽量改为===
。
学习资料
下期议题精选(任选其一)
- 基于electron开发桌面应用分享(类似vscode)
- nodejs相关分享
- mvvm框架架构思想分享
- 游戏开发中核心算法分享
- 各类游戏开发优化分享