拥抱TypeScript

329 阅读4分钟

TypeScript入门

基本类型和扩展类型

  1. TypescriptJavascript共享相同的基本类型,但有一些额外的类型。
  • 元组Tuple
  • 枚举enum
  • AnyVoid
  • never

基本类型

```
// 数字
let num: number = 6;
let num: number = 0xf00d;

// 字符串
let name: string = "bob";

// 数组,第二种方式是使用数组泛型,Array<元素类型>:
let list: number[] = [1, 2, 3];
let list: Array<number> = [1, 2, 3];
let list: any[] = [1,2,3,'4'];

// symbol
let s:symbol = Symbol('s');

// boolean
let isTrue: boolean = true;

// undefined
let u: undefined = undefined;

// null
let n: null = null;

// 定义多种类型
let s:string|undefined;
```

扩展类型

  1. 元组Tuple

    元组作为有组织的数组,需要以正确的顺序预定义数据类型。

    const tuple: [number, string, string] = [24, "Indrek" , "Lasn"];
    const arr3:[string, number] = ['12',1];
    
  2. 枚举enum

    enum类型是对JavaScript标准数据类型的一个补充。 像C#等其它语言一样,使用枚举类型可以为一组数值赋予友好的名字。

    enum Flag {
        success = 0,
        error = 1
    }
    let c: Flag = Flag.success; // 0
    let d: string = Flag[0]; // success
    
  3. Any

    表示任意类型,慎重使用,用多了就失去使用Ts的意义

    用于类型不明确的情况

  4. Void

    • 在Typescript中,你必须在函数中定义返回类型
    • 我们可以将其返回值定义为void
    • 用void定义的函数不能写return
    function run():void{
        console.log('run')
    }
    
  5. never

    Never是永远达不到的值

    // 返回never的函数必须存在无法达到的终点
    function error(message: string): never {
        throw new Error(message);
    }
    
    // 推断的返回值类型为never
    function fail():never {
        return error("Something failed");
    }
    
    // 返回never的函数必须存在无法达到的终点
    function infiniteLoop(): never {
        while (true) {
        }
    }
    

函数

  1. 函数声明
    function run():string {
        console.log('ru')
        return 'su'
    }
    
  2. 函数表达式
    const run = function():void{
        console.log('run')
    }
    
  3. 可选参数
  • ES5中的实参和形参个数可以不一样,TS中必须一致。如果不一致必须设置可选参数
  • 可选参数必须位于非可选参数后
    function getInfo(name:string, age?: number):string{
        if(age) {
            return `${name}---${age}`
        } else {
            return `${name}---年龄无`
        }
    }
    
  1. 默认参数

    ES6中提供了默认参数的方法,TS中也可以使用

    function getInfo(name:string, age: number=20):string{
        return `${name}---${age}`
    }
    
  2. 剩余参数

    ES6中的参数解构

    function sum(...args:number[]):number {
        let num:number = 0;
        for (let i = 0;i < args.length; i++) {
            num += args[i]
        }
        return num
    }
    
  3. 函数重载

  • 在JAVA中函数的重载是函数名称相同,参数不同。通过传的参数来执行不同的方法

  • TS为了兼容JS,重载写法不同

    // 定义方法参数及返回值类型
    function getInfos(name:string):string;
    function getInfos(age: number):number;
    
    // 判断执行哪个函数分别执行
    function getInfos(str:any):any {
        if (typeof str === 'string') {
            return str // name值
        } else {
            return str // age值
        }
    }
    
  1. 箭头函数

    特性 同ES6中的箭头函数

  1. ES5中的类

    function Person(name, age) {
        this.name = name
        this.age = age
    }
    Person.prototype.say = function() {
        console.log(this.name + 'hello')
    }
    // 添加静态方法
    Person.print = function() {
        console.log(print)
    }
    
  2. ES5的继承

    funtion Man(name, age, sex) {
        this.sex = sex
        Person.call(this, name, age)
    }
    Man.prototype = Object.create(Person.prototype, {
        constructor: {
            value: Man
        }
    })
    
  3. TS中的类

    class Person {
        name:string; // 属性,省略了public关键字
        age: number;
        constructor (name:string, age:number) { // 构造函数,在实例化的时候执行
            this.name = name;
            this.age = age;
        }
    
        say():void {
            console.log(this.name + 'hello')
        }
    
        getName():string{
            return this.name
        }
    
        setName(name:string):void {
            this.name = name
        }
    }
    
  4. TS中的继承

    class Man extends Person{
        sex:boolean;
        constructor (name:string,age:number,sex:boolean) {
            super(name, age); // 初始化父类的构造函数
            this.sex = sex;
        }
    
        say():void {
            console.log(this.name + 'hello,我是子类的')
        }
    }
    
  5. 类中的修饰符

    在typescipt中,定义属性时,提供了三种访问修饰符

    • public 公有的,都可以访问
    • protected 在类里面,子类里面可以访问,类外部无法访问
    • private 在类里面可以访问,子类和外部都不能访问

    属性如果不加修饰符,默认为public

  6. 类中的静态方法与静态属性

    用static修饰

  7. 多态

    父类定义一个方法没有实现,每一个子类都有不同的实现

  8. 抽象类

  • 抽象类是提供其他类继承的基类,不能被实例化
  • 用abstract关键字定义抽象类和抽象方法,抽象类中的抽象方法不包含具体实现并且必须在派生类中实现
  • 抽象方法必须在抽象类中
    abstract class Animal {
        name:string;
        constructor(name:string) {
            this.name = name
        }
        abstract eat():any;
    }
    
    class Dog extends Animal{
        constructor(name:string) {
            super(name)
        }
        eat():void {
            console.log(this.name + 'eat')
        }
    }
    

接口

在面向对象的编程中,接口是一种规范定义,定义了行为和动作的规范。在程序设计里,接口起到限制和规范的作用

  1. 属性类接口
    interface FullName {
        firstName:string;
        lastName:string;
        age?: number; // 接口的可选属性
    }
    
    function getName(name:FullName): void {
        console.log(name.firstName + name.lastName)
    }
    
    getName({firstName: '张三', lastName: 'hehe'});
    
  2. 函数类接口
    interface Encrypt {
        (key:string, value:string):string
    }
    
    const md5:Encrypt = function (key:string, value:string):string {
      console.log(key + value);
      return key + value
    };
    
  3. 类类型接口 有点类似抽象类
    interface Animal {
        name: string
        eat(str:string): void
    }
    
    class Dog implements Animal{
        name:string;
        constructor(name:string) {
            this.name = name
        }
        eat(str: string): void {
            console.log(this.name + '吃');
        }
    }
    
  4. 接口的继承
    interface Animal {
        eat():void
    }
    
    interface People extends Animal{
        work():void
    }
    
    class Web implements People{
        constructor() {
    
        }
        eat(): void {
        }
        work(): void {
        }
    }
    

泛型

要创建一个可重用的组件,其中的数据类型就必须要兼容很多的类型,那么如何兼容呢,TypeScript提供了一个很好的方法:泛型

要兼容多种数据格式,可能会有人想到any

function getData(value:any):any{
    return value
}

存在有可能传入的值和返回的值不是同一种值,例如,传入数字,但是不确定返回的是什么值

// 定义泛型
function getData<T>(args:T):T {
    return args
}

const data = getData<number>(123)
const data = getData(123) // 类型推断

泛型类

class FilteredList<T> {
    // 声明过滤器是以 T 为参数类型,返回 boolean 的函数表达式
    filter: (v: T) => boolean;
    // 声明数据是 T 数组类型
    data: T[];
    constructor(filter: (v: T) => boolean) {
        this.filter = filter;
    }

    add(value: T) {
        if (this.filter(value)) {
            this.data.push(value);
        }
    }

    get all(): T[] {
        return this.data;
    }
}

// 处理 string 类型的 FilteredList
const validStrings = new FilteredList<string>(s => !s);

// 处理 number 类型的 FilteredList
const positiveNumber  = new FilteredList<number>(n => n > 0);

泛型接口

// 方式1
interface Config {
    <T>(value:T):T;
}

const setData:Config = function<T>(value:T):T {
    return value
};
setData<number>(123)

// 方式2
interface Config2<T> {
    (value:T):T
}

function getData<T>(value:T):T {
    return value
}

const myGetData:Config2<string> = getData;

把类当做参数的泛型类

class User {
    username: string;
    password: string;
    constructor(username:string, password:string) {
        this.username = username;
        this.password = password
    }
}

class MysqlDb<T> {
    add(info:T): boolean {
        console.log(info);
        return true
    }
}

const u = new User('hedekun', '123456');
const db = new MysqlDb<User>();
db.add(u);

装饰器

装饰器是一种特殊类型的声明,它能够被附加到类声明,方法, 访问符,属性或参数上,可以修改类的行为。 装饰器使用 @expression这种形式,expression求值后必须为一个函数,它会在运行时被调用,被装饰的声明信息做为参数传入。

  1. 类装饰器
function Path(path: string) {
    return function (target: Function) {
        !target.prototype.$Meta && (target.prototype.$Meta = {})
        target.prototype.$Meta.baseUrl = path;
    };
}

@Path('/hello')
class HelloService {
    constructor() {}
}

console.log(HelloService.prototype.$Meta);// 输出:{ baseUrl: '/hello' }
let hello = new HelloService();
console.log(hello.$Meta) // 输出:{ baseUrl: '/hello' }
  1. 属性装饰器 接受两个参数
  • 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
  • 成员的名字。
function logProperty(params:string) {
    return function (target: any,key:string) {
        let _val = this[key];//属性值
        const getter = function () {
            console.log(`Get:${key} => ${_val}`);
            return _val;
        };
        const setter = function () {
            console.log(`Set:${key} => ${params}`);
            _val = params;
        };
        //删除属性,在严格模式下,如果对象是不可配置的,将会抛出一个错误。否则返回false
        if (delete this[key]) {
            Object.defineProperty(target, key, {
                get: getter,
                set: setter,
                enumerable: true,
                configurable:true
            })
        }
    }
}


class Test {
    @logProperty('taobao')
    public url:any = 'http://www.baidu.com';
    constructor() {
        // console.log(this.url)
    }
    getData():void {
        console.log(this.url)
    }
}

const t:any = new Test();
t.getData();
  1. 方法装饰器 它会被应用到方法的 属性描述符上,可以用来监视,修改或者替换方法定义。 方法装饰会在运行时传入下列3个参数:
  • 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
  • 成员的名字。
  • 成员的属性描述符{value: any, writable: boolean, enumerable: boolean, configurable: boolean}。
function get(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    const oMethods = descriptor.value;
    descriptor.value = function (...args:any[]) {
        args = args.map(item => String(item));
        oMethods.call(this,...args)
    }
}

class Hello{
    name: string;
    age: number;
    constructor() {
        console.log('hello');
        this.name = 'yugo';
    }
    
    @get
    hello(...args:any[]){
        console.log(args);
        console.log('这是原来的方法')
    }

}

const h:any = new Hello();
h.hello(123, 'xxx');
  1. 方法参数装饰器 参数装饰器表达式会在运行时当作函数被调用,传入下列3个参数:
  • 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
  • 参数的名字。
  • 参数在函数参数列表中的索引。
function PathParam(paramName: string) {
    return function (target, methodName: string, paramIndex: number) {
        !target.$Meta && (target.$Meta = {});
        target.$Meta[paramIndex] = paramName;
    }
}

class HelloService {
    constructor() { }
    getUser( @PathParam("userId") userId: string) { }
}

console.log((<any>HelloService).prototype.$Meta); // {'0':'userId'}

装饰器执行顺序

function ClassDecorator() {
    return function (target) {
        console.log("I am class decorator");
    }
}
function MethodDecorator() {
    return function (target, methodName: string, descriptor: PropertyDescriptor) {
        console.log("I am method decorator");
    }
}
function Param1Decorator() {
    return function (target, methodName: string, paramIndex: number) {
        console.log("I am parameter1 decorator");
    }
}
function Param2Decorator() {
    return function (target, methodName: string, paramIndex: number) {
        console.log("I am parameter2 decorator");
    }
}
function PropertyDecorator() {
    return function (target, propertyName: string) {
        console.log("I am property decorator");
    }
}

@ClassDecorator()
class Hello {
    @PropertyDecorator()
    greeting: string;


    @MethodDecorator()
    greet( @Param1Decorator() p1: string, @Param2Decorator() p2: string) { }
}

输出结果

I am property decorator
I am parameter2 decorator
I am parameter1 decorator
I am method decorator
I am class decorator
  1. 有多个参数装饰器时:从最后一个参数依次向前执行

  2. 方法和方法参数中参数装饰器先执行。

  3. 类装饰器总是最后执行。

  4. 方法和属性装饰器,谁在前面谁先执行。因为参数属于方法一部分,所以参数会一直紧紧挨着方法执行。