安装

npm install typescript

生成配置文件:tsconfig.json

npx tsc --init

ts中的数据类型

  • boolean:布尔
  • number:数字
  • string:字符串
  • array:数组
  • tuple:元组
  • enum:枚举
  • any:任意类型
  • null 和 undefined
  • void
  • never

数据类型定义方法

定义原始类型

boolean、number、string

const flag: boolean = false;

定义数组类型

第一种方式:

const sArr: string[] = ['1', 'dhdh'];
let nArr: number[] = [1, 2, 10];

// 定义任意类型的数组
let aArr: any[] = ['dd', 123];

第二种方式(使用泛型):

let arr: Array<number> = [1, 45, 67];
let arr: Array<any> = ['dd', 123];

定义元组类型

属于数组的一种,定义数组中每一项的类型

let arr: [string, number, boolean] = ['哈哈', 23, false];

定义枚举类型

如果给枚举类型赋值了,使用的是赋值的数据

enum Flag {
    success = 1,
    error = -1
}

let flag: Flag = Flag.success;  // 1

如果没有给枚举类型赋值,使用的是索引值(index)

enum Color {
    red, blue, orange
}

let a: Color = Color.red;  // 0
let b: Color = Color.blue;  // 1
let c: Color = Color.orange;  // 2

如果给其中某写枚举值赋值了,其他一些枚举值没有赋值,后面的枚举值会在前面已经定义的枚举值基础上递增

enum Color {
    red,
    blue = 5,
    orange
}

let a: Color = Color.red;  // 0
let b: Color = Color.blue;  // 5
let c: Color = Color.orange;  // 6

定义任意类型

let num: any = 123;
num = '456';
num = false;

定义 null 和 undefined 类型

其他(never类型)数据类型的子类型

var num: undefined;
console.log(num);
var num: number | undefined;
num = 123;
console.log(num);

定义 void 类型

一般用于定义没有返回值的方法

function run(): void {
    console.log('我没有return哦');
}

定义其他类型(never)

是其他类型(包括 null 和 undefined)的子类型,表示从不会出现的值。

这个类型基本不会用到。

var a: undefined;
a = undefined;
var a: never;

a = (() => {
    throw new Error('报错');
})();

函数的定义

定义返回值

function run(): string {
    return 'haha';
}

const fun = function(): number {
    return 123;
}

定义传参

function run(name: string, age: number): string {
    return `${name}--${age}`;
}

const fun = function(name: string, age: number): string {
    return `${name}--${age}`;
};

// 加上 ? 表示可选参数,可传可不传
// 【注意】可选参数必须放在参数的最后面!!!
const fun2 = function(name: string, age?: number): string {
    if (age) {
        return `${name}--${age}`;
    } else {
        return `${name}--保密`;
    }
};

// 默认参数
const fun3 = function(name: string, age: number = 20): string {
    if (age) {
        return `${name}--${age}`;
    } else {
        return `${name}--保密`;
    }
};

// 剩余参数
const fun4 = function(a, b, ...rest:number[]): void {
    let sum = 0;
    for (let i = 0; i < rest.length; i++) {
        sum += rest[i];
    }
    console.log(a, b, sum);
};
fun4(1, 2, 3, 4);  // 1 2 7

// 函数重载
function getInfo(age: number): string;
function getInfo(name: string): string;
function getInfo(str: any):any {
    if (typeof str === 'number') {
        return `我今年${str}岁`;
    } 
    return `我叫${str}`;
}

getInfo(12);  // 我今年12岁
getInfo('明明');  // 我叫明明
// 报错,因为没有定义 boolean 类型
// getInfo(false); 

ts中的类

类的定义

class Person {
    name: string;  // 属性 前面省略了public关键词

    constructor(name: tring) {
        this.name = name;
    }

    getName(): void {
        console.log(this.name);
    }

    setName(name: string): void {
        this.name = name;
    }
}

const p = new Person('张三');
p.getName();  // 张三
p.setName('李四');
p.getName();  // 李四

继承:extends + super

class Person {
    name: string;  // 属性 前面省略了public关键词

    constructor(name: tring) {
        this.name = name;
    }

    run(): string {
        return this.name;
    }
}

class Web extends Person {
    constructor(name: string) {
        super(name);
    }
}

const w = new Web('张三');
w.run();  // 张三

类里面的修饰符

public、protected、private

属性的修饰符默认是公有(public)。

ts中的接口

使用 interface 关键字定义接口。

属性接口

interface FullName {
    firstName: string;
    secondName: string;
}

function printName(name: FullName) {
    console.log(name.firstName + '--' + name.secondName);
}

// 这种写法不允许对象中传入多余的属性
printName({
    firstName: '张',
    secondName: '三',
})

// 待确认?:可以传入其他的属性
const obj = {
    age: 18,
    firstName: '张',
    secondName: '三',
};
printName(obj);

可选属性

interface FullName {
    firstName: string;
    secondName: string;
    // 可选属性
    age?: number;
}

function getName(name: FullName) {
    console.log(name.firstName + name.secondName + name.age);
}

getName({
    firstName: '张',
    secondName: '三',
})

getName({
    firstName: '张',
    secondName: '三',
    age: 18,
})

函数类型接口

interface encrypt {
    (key: string, value: string): string;
}

const md5: encrypt = function(key: string, value: string): string {
    return key + value;
};
console.log(md5('name', 'zhangsan'));

const test: encrypt = function(key: string, value: string): string {
    return key + '--' + value;
};
console.log(test('name', 'zhangsan'));

可索引接口:数组、对象的约束(不常用)

// 数组约束
interface UserArr {
    // 规定数组每一项数据都是 string 类型
    [index: number]: string;
}

const arr: UserArr = ['aaa', 'bbb'];
// const arr: UserArr = [123, 'bbb'];  // 错误写法

// 对象约束
interface UserObj {
    [index: string]: string;
}

const obj: UserObj ={
    name: '张三',
};

类类型接口

interface Animal {
    name: string;
    eat(str: string): void;
}

class Dog implements Animal {
    name: string;

    constructor(name: string) {
        this.name = name;
    }

    // 虽然定义传了参数,但是不传参数也是可以的
    // 虽然可以不传参数,但是这个方法必须定义
    eat() {
        console.log(this.name + '吃粮食');
    }
}

const d = new Dog('旺财');
d.eat();

接口扩展:接口可以继承接口

interface Animal {
    eat(): void;
}

interface Personal extends Animal {
    work(): void;
}

class Web implements Personal {
    public name: string;

    constructor(name: string) {
        this.name = name;
    }

    // 当类所继承的接口继承了其他接口的时候,这个类还必须实现其父级接口的方法
    eat() {
        console.log(this.name + '吃');
    }

    work() {
        console.log(this.name + '工作');
    }
}

const w = new Web('李四');
w.eat();
w.work();

ts中的泛型

作用是校验类型,却不固定类型。一般用于模板通用方法。

泛型函数

简单示例:

function a<T>(value: T): T {
    return value;
}

泛型类

// 这里的 T 就是泛型,通过对最外面的 T 的类型的定义,决定了其内部的所有的 T 的类型的定义。
class Min<T> {
    public list: T[] = [];

    add(value: T): void {
        this.list.push(value);
    }

    min(): T {
        let minNum = this.list[0];
        for (let i = 0; i < minNum.length; i++) {
            if (minNum > this.list[i]) {
                minNum = this.list[i];
            }
        }
        return minNum;
    }
}

const m1 = new Min<number>();
m1.add(12);
m1.add(3);
m1.add(9);
console.log(m1.min());  // 3

const m2 = new Min<string>();
m2.add('j');
m2.add('v');
m2.add('a');
console.log(m2.min());  // a

泛型接口

第一种写法:

interface ConfigFn {
    <T>(value: T): T;
}

const getData: ConfigFn = function<T>(value: T): T {
    return value;
}

getData<string>('张三');

第二种写法:

interface ConfigFn<T> {
    (value: T): T;
}

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

const myGetData: ConfigFn<string> = getData;

myGetData('20');

装饰器

装饰器是一种特殊类型的声明,它能够被附加到类声明,方法,属性或者参数上,可以修改类的行为。

常见的装饰器有:类装饰器,属性装饰器,方法装饰器,参数装饰器。

类装饰器

普通装饰器

// 装饰器
function logClass(params: any) {
    console.log(params);  // 打印的就是被装饰的类
    params.prototype.apiUrl = '动态扩展的属性';
}

@logClass
class HttpClient {
    constructor() {

    }

    getData() {

    }
}

const http: any = new HttpClient();
console.log(http.apiUrl);

装饰器工厂

可以传入参数

function logClass(params: string) {
    return  function(target: any) {
        console.log(target);  // HttpClient
        console.log(params);  // hello
    };
}

@logClass('hello')
class HttpClient {
    constructor() {

    }

    getData() {

    }
}

属性装饰器

属性装饰器表达式会在运行时当做函数被调用,传入下列两个参数:

  1. 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
  2. 成员的名字。
function logProperty(params: any) {
    return function(target: any, attr: any) {
        console.log(target);  // HttpClient
        console.log(attr);  // 'url'

        target[attr] = params;  // 修改URL
    }
}

class HttpClient {
    @logProperty('http://www.baidu.com')
    public url : anty | undefined;

    constructor() {}

    getData() {
        console.log(this.url);
    }
}

const http = new HttpClient();
http.getData();  // 'http://www.baidu.com'

方法装饰器

它被应用到方法的属性描述符上,可以用来监视,修改或者替换方法定义。方法装饰器会在运行时传入下列三个参数:

  1. 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
  2. 成员的名字。
  3. 成员的属性描述符。
function logMethod(params: any) {
    return function (target: any, methodName: any, desc: any) {
        console.log(target);
        console.log(methodName);
        console.log(desc);  // getData

        // 添加属性和方法
        target.apiUrl = 'xxx';
        target.run = function() {
            console.log('run');
        };

        // 修改装饰器的方法,把装饰器方法里面传入的所有参数改为 string 类型
        // 先将原始方法保留
        const oMethod = desc.value;
        // 修改方法
        desc.value = function (...args: any[]) {
            args = args.map(item => {
                return String(value);
            });

            // 对象冒充
            // 如果去掉这句话,原始方法就不会执行了,等同于替换了原始方法;加上这句话,会先执行这个被修改的方法,然后运行原始方法
            oMethod.apply(this, args);
        }
    };
}

class HttpClient{
    public url: any | undefined;

    constructor() {}

    @logMethod('http://www.baidu.com')
    getData() {
        consoe.log(this.url);
    }
}

const http: any = new HttpClient();
console.log(http.apiUrl);
http.run();

参数装饰器

参数装饰器表达式会在运行时当做函数调用,可以使用参数装饰器为类的原型增加一些元素数据,传入下列3个参数:

  1. 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
  2. 方法的名字。
  3. 参数在函数参数列表中的索引。
function logParams(params: any) {
    return function (target: any, methodName: any, paramsIndex: any) {
        console.log(params);  // haha
        console.log(target);  // HttpClient
        console.log(methodName);  // getData
        console.log(paramsIndex);  // 0

        targrt.apiUrl = params;
    };
}

class HttpClient {
    public url: any | undefined;

    constructor() {}

    getData(@logParams('haha') uuid: any) {
        console.log(uuid);  // 123456
    }
}

const http = new HttpClient();
http.getData(123456);
console.log(http.apiUrl);  // haha

装饰器执行顺序

属性 》方法 》方法参数 》类。

如果有多个同样的装饰器,在后面的装饰器会先执行。

参考链接

Typescript教程.