前端杂货铺

走进TypeScript

2020-02-02  本文已影响0人  写前端的大叔

如今前端技术层出不穷,es6还没完全掌握,TypeScript又来了,2020了,不会点TypeScript都不好意思说自己是做前端的了。虽然技术层出不穷,但万变不离其宗,根据多年的开发经验,学习新技术其实也不是那么难,大多都差不多的,趁放假有时间,来了解下TypeScript,等上班的时候就可以用上了。TypeScriptJavaScript 的一个超集,支持 ECMAScript 6 标准。其实跟swift差不多,下面就来整理TypeScript相关的知识点。

1.数据类型

TypeScript的数据类型跟JavaScript差不多,有数字,字符串,布尔值,数组等类型,还有结构体、元组、枚举类型等。

1.1.基础数据类型

定义数字,字符串,布尔值,数组等类型时,跟JavaScript差不多,只是定义的语法稍微有一点点区别。如下所示:

let isLike: boolean  = false;
let index: number = 1;
let firstName: string = 'haha';
let result: string = `my name is ${firstName}`;
let list:Array<number> = [1,2,3]

1.2.元组

元组是一个已知元素数量和类型的数组,各类型的元素不必相同,当访问一个已知索引的元素,会得到正确的类型,当访问一个越界的元素时,不会报错,会使用联合类型替代。

let array: [number, string] = [12, 'haha'];
console.log(array[0]);//12
console.log(array[1]);//haha
array[3] = 'ha';
console.log(array[3]);//ha

1.3.枚举

枚举类型是为一组数值赋予友好的名字。使用关键字enum进行定义,如下所示:

enum Color { RED, GREEN, BLUE };
let color: Color = Color.RED;
console.log(color);//0

默认情况下是从0 开始的,也可以手动设置成员的数值,如下所示:

enum Color { RED = 1, GREEN, BLUE };
let color: Color = Color.RED;
console.log(color);//1

除了获取其成员的数值外,也可以通过数值来访问枚举的名称,如下所示:

enum Color { RED = 1, GREEN, BLUE };
console.log(Color[1]);//RED

1.4.Any

Any代表任意类型的变量,它允许你在编译时可选择地包含或移除类型检查,比如将一个数组指定为Any时,可以往数组中添加任意类型的数据。

let array: Array<any> = [1, 'haha', true, { a: 'aa' }];
console.log(array)

1.5.Void

Void类型与Any相关,表示没有任何类型,比如定义函数时,没有返回时,默认的类型为Void类型。

function test() :void {
    console.log('haha');
}
test();

1.5.Null 和 Undefined

TypeScript里,undefinednull两者各自有自己的类型分别叫做undefinednull。 和 void相似,它们的本身的类型用处不是很大。默认情况下nullundefined是所有类型的子类型。 就是说你可以把 nullundefined赋值给number类型的变量。

1.6.Never

never是表示那些永不存在的类型的值,一般用在抛出异常时,如下所示:

// 返回never的函数必须存在无法达到的终点
function error(message: string): never {
    throw new Error(message);
}

1.6.Object

Object表示那些除基本数据外的类型,也就是除numberstringbooleansymbolnullundefined之外的类型。

1.7.类型断言

类型断言相当于类型转换,它没有运行时的影响,只是在编译阶段起作用。类型断言有两种方式,一种是会用<>,一种是使用as。如下所示:

let str: any = 'this is string';
let len: number = (<string>str).length;
var lenght: number = (str as string).length;    
console.log(len);//14
console.log(lenght);//14

1.8泛型

泛型是一种创建可复用代码组件的工具,使用代码能提供代码的可复用性和通用性。用过泛型能有效增加类、类型和接口的使用能力。最常用的是在数组中限定类型,如下所示:

class Person {
    age: number;
}
class Animal {
    name: string;
}

let array: Array<Person> = [];
let person: Person = new Person();
let animal: Animal = new Animal();
array.push(person);
array.push(animal);//error

定义数组的时候,使用<>定义了一个泛型,只能往数组中添加Person的对象,如果往数组中添加Animal的对象将报错。
泛型也是类型,泛型函数的类型与非泛型函数的类型没什么不同,只是有一个类型参数在最前面,像函数声明一样:

function identity<T>(arg: T): T {
    return arg;
}

let myIdentity: <T>(arg: T) => T = identity;

2.接口

TypeScript的核心之一是对值所具有的类型进行检查,在TypeScript里,可以通过定义接口来设定一些类型规则。如下所示:

interface Person { 
    name:string
}

function test(person: Person) { 
    console.log(person.name);//haha
}

var obj = { name: 'haha', age: 10 }
test(obj);

如上代码所示,test函数中有一个person参数,并且类型为Person,该参数做了限制,传给函数的参数必须要有 name属性,否责会报错。

2.1可选属性

上面的代码定义的 name属性是必须的,如果传给函数的参数没有name属性会报错,可以通过可选属性来解除这种限制,可选属性使用?来设置。如下所示:

interface Person { 
    name?: string
    age?:number
}

function test(person: Person) { 
    console.log(person.name);//haha
}

test({age: 10 });

2.2只读属性

在定义变量的时候,可以使用readonly来限制外部修改属性值,在创建的时候赋值后不能再修改。如下所示:

interface Person { 
    readonly name: string
    age:number
}

var obj = { name: 'haha', age: 10 }
obj.name = 'hhhh'//error

2.3函数类型

接口除了描述带有普通属性的普通对象外,也可以描述函数类型。为了使用接口表示函数类型,需要给接口定义一个调用签名。它就像是一个只有参数列表和返回值类型的函数定义。参数列表里的每个参数都需要名字和类型。如下所示:

interface PersonInfo { 
    (name:string,age:number):string
}

定义好函数类型后,就可以像使用其它接口一样使用这个函数类型的接口,如下所示:

interface PersonInfo { 
    (name:string,age:number):string
}

let personInfo: PersonInfo;
personInfo = function (name: string, age: number): string { 
    var result: string;
    result = `name:${name}
    age:${age}`
    return result;
}

console.log(personInfo('haha', 12));

2.4可索引类型

可索引类型具有一个 索引签名,它描述了对象索引的类型,还有相应的索引返回值类型。如下所示:

interface StringArray { 
    [index:number]:string
}
let array: StringArray = ['haha1', 'haha2']
console.log(array[1]);//haha2

TypeScript支持两种索引签名,分别为数字和字符串。但是数字索引返回的类型必须是字符串索引返回类型的子类。这是因为用数字是索引时,javascript会默认将数字转换为字符串后再去索引对象。

class Animal {
    name: string;
}
class Dog extends Animal {
    breed: string;
}

// 错误:使用数值型的字符串索引,有时会得到完全不同的Animal!
interface NotOkay {
    [x: number]: Animal;
    [x: string]: Dog;
}

2.5类类型

TypeScript也能够使用接口来明确的强制一个类去符合某种契约。定义好接口后,使用implements来实现接口,然后在类中添加接口的属性和方法,如下所示:

interface Person { 
    name: string,
    eat();
}
class Student implements Person { 
    name: string;
    eat() { 
        console.log(`${this.name}在吃东西`)
    }
}

var stu = new Student();
stu.name = 'haha';
stu.eat();//haha在吃东西

2.6接口继承

接口也是可以继承,并且可以多重继承,可以很方便的将一个接口里的成员复制到另一个接口中,如下所示:

interface Shape {
    color: string;
}

interface PenStroke {
    penWidth: number;
}

interface Square extends Shape, PenStroke {
    sideLength: number;
}

let square = <Square>{};
square.color = "blue";
square.sideLength = 10;
square.penWidth = 5.0;

3.类

TypeScript中的类跟ES6中的类差不多,只是定义属性的方式稍微有点差别,以下为 TypeScript中定义的类。

class Animal { 
    name: string;
    constructor(name: string) { 
        this.name = name;
    }
    eat() { 
        console.log('动物会吃东西');
    }
}

class Dog extends Animal { 
    constructor(name: string) { 
        super(name);
    }
    eat() { 
        console.log(this.name +' 在吃东西');
    }
}

class Cat extends Animal { 
    constructor(name) { 
        super(name)
    }
    eat() { 
        console.log(this.name +' 在吃东西');
    }
}

let dog: Dog = new Dog('haha');
dog.eat();
let cat: Animal = new Cat('cat')
cat.eat();

.3.1变量修饰符

TypeScript里可以使用public,private,protected,readonly修饰成员变量,默认为public

  • public:默认的修饰符,外部可以访问。
  • private:当成员被标记成 private时,它就不能在声明它的类的外部访问。
  • protectedprotected修饰符与 private修饰符的行为很相似,但有一点不同, protected成员在派生类中仍然可以访问。
    -readonlyreadonly关键字将属性设置为只读的。 只读属性必须在声明时或构造函数里被初始化。

3.2存取器

TypeScript支持通过getters/setters来截取对对象成员的访问。 它能帮助你有效的控制对对象成员的访问。

class Animal { 
    name: string;
    private _fullName: string;
    constructor(name: string) { 
        this.name = name;
    }
    set fullName(value) { 
        console.log('set fullname')
        this._fullName = value;
    }

    get fullName() { 
        console.log('get fullname')
        return this._fullName;
    }
    eat() { 
        console.log('动物会吃东西');
    }
}

fullName添加了getset方法。
3.3 static
使用static可以定义静态属性和静态方法。

class Animal { 
    static fullName: string;
    static eat() { 
        console.log(Animal.fullName)
    }
}
Animal.fullName = 'haha'
Animal.eat();//haha

4.类型推论

TypeScript里,在使用变量时可以推断出变量属性什么类型。

4.1自动推论:

在对属性和成员进行初始化时,可以进行自动推论,如下所示,将推断出变量x的类型为number类型。

let x = 12;

4.2最佳通用类型

当需要从几个表达式中推断类型的时候,会使用这些表达式的类型来推断出一个最合适的通用类型。如:

let x = [0, 1, null];

如果没有找到最佳通用类型,类型推断的结果为联合数组类型。

5.类型兼容性

TypeScript里的类型兼容性是基于结构子类型的。 结构类型是一种只使用其成员来描述类型的方式。 它正好与名义(nominal)类型形成对比。比如下面的两个类,默认被当成同一个类型。

class Person {
    name: string;
}
class Animal {
    name: string;
}

这是因为在基于名义类型的类型系统中,数据类型的兼容性或等价性是通过明确的声明和/或类型的名称来决定的。这与结构性类型系统不同,它是基于类型的组成结构,且不要求明确地声明。

6.高级类型

6.1交叉类型

交叉类型是将多个类型合并成一个类型,它包含了所有类型的特性。如下所示:

class Person {
    name: string;
}
class Animal {
    name: string;
    age: number;
    run() {
        console.log('running')
     }
}
function extend<T, U>(first: T, second: U): T & U {
    let result = <T & U>{};
    for (let key in first) {
        (<any>result)[key] = (<any>first)[key]
    }

    for (let key in second) { 
        (<any>result)[key] = (<any>second)[key];
    }
    return result;

}

var obj = extend(new Person(), new Animal());
obj.run();

6.2联合类型

联合类型表示一个值可以是几种类型之一,使用|分隔每个类型。所以 number | string | boolean表示一个值可以是 numberstring,或 boolean。如果一个值是联合类型,我们只能访问联合类型中所有类型的共有成员,如下所示:

class Person {
    name: string;
    eat() {
        console.log('Person eat')
    }
    
}
class Animal {
    name: string;
    age: number;
    run() {
        console.log('running')
     }
    eat() {
        console.log('Animal eat')
     }
}

function getObj(): Person | Animal{ 
    return new Animal();
}
let obj1 = getObj();
obj1.eat();
obj1.run();//error

个人博客

上一篇 下一篇

猜你喜欢

热点阅读