day7 深入浅出TypeScript | 青训营笔记

85 阅读7分钟

TypeScript

基本概念

  • 是js的超集
  • 引入了类型的概念,更严格
  • 编译时转成js

手动编译
tsc .\文件名
自动编译
tsc --init生成配置文件,可在emit里修改js输出路径。
terminal->run task->show all tasks->tsc:watch

类型声明

原始数据类型

let isDone: boolean = false;

其他原始数据类型与此相似,都是在原来的声明中加入:类型
nullundefined可作为其他类型的子类型。 也就是说 undefined 类型的变量,可以赋值给 number 类型的变量:

// 这样不会报错
let num: number = undefined;
// 这样也不会报错
let u: undefined;
let num: number = u;

引用数据类型
数组

  1. 类型+[]
let arr1:[] = [];
let arr2:number[] = [1,2,3]
  1. 泛型
let arr:Array<number> = [1,2,3];

对象

let obj:object = {};
obj = null;
obj = undefined;
obj = [];
obj = new String();//new 一个实例对象
obj = String;

any
任意类型

void
空值,无返回值的函数

类型推论

  • 有赋值,会找到对应的类型
  • 无复制,any

联合类型

可以同时声明这个变量为多种类型中的一种。

let myFavoriteNumber: string | number;
myFavoriteNumber = 'seven';
myFavoriteNumber = 7;

但方法必须是对应类型的方法,当类型不确定时,只能使用共同的方法。

接口 interface

对行为的抽象,可以理解为一种约束。

对象接口

  • 接口首字母大写,有些前面会加上I
  • 可选属性加上?
  • 任意属性
  • 只读属性
// 对象接口
interface Person{
    readonly id: number, //只读属性,修改会报错
    name: string,
    age: number,
    sex?: string,//可选属性
    // 任意属性
    // [propName:string]: string 其他属性非string会报错
    // [propName:string]: any
    [propName:string]: number | string
}

let p:Person={
    id: 10,
    name:'张三',
    age: 18, 
    weight:100
}
// p.id = 11 报错

一个接口中只能定义一个任意属性。如果接口中有多个类型的属性,则可以在任意属性中使用联合类型。

数组接口

interface INewArray{
    [index:number]:number
}

let arr:INewArray = [1,2,3,4]

函数接口

(参数:类型,...):返回类型

interface ISearch{
    (a:string,b:string):boolean
}

const fun1:ISearch = function(a:string,b:string):boolean{
    return a.search(b) !== -1;
}

函数

函数声明,命名函数

function add1(a:number,b:number):number{
    return a+b;
}

函数表达式,匿名函数

const add2 = function(a:number,b:number):number{
    return a+b;
}

完整写法,对函数也声明类型

const add3:(a:number,b:number)=>number = function(a:number,b:number):number{
    return a+b;
}

可选参数和默认参数

// 可选参数 ?
function getName(x:string,y?:string):string{
    return x+y;
}

// 默认参数
function getName2(x:string='李',y?:string):string{
    return x+y;
}

可选参数必须放最后

剩余参数和函数重载

剩余参数用...表示。
函数重载:重载允许一个函数接受不同数量或类型的参数时,作出不同的处理。
即定义多个函数类型。

function add(x:string,y: string):string
function add(x:number,y: number):number
function add(x:string | number,y: string | number):string | number {
    if(typeof x == 'string' && typeof y === 'string'){
        return x + y;
    }else if(typeof x == 'number' && typeof y === 'number'){
        return x + y;
    }
}

console.log(add(1,2));
console.log(add('a','b'));

类型断言

用途

  • 将一个联合类型断言为其中一个类型,可以访问其内部的方法。

一种是利用as,另一种是<类型>变量

function getLength(x:number | string){
    if((x as string).length){
        // return (x as string).length;
        return (<string>x).length
    }else return x.toString.length;
}

console.log(getLength('asdf'));
  • 将一个父类断言为更具体的子类
  • 将一个类型断言为any,可访问任何属性和方法
//为window添加foo属性
window.foo = 1; //会报错,window不存在foo属性
(window as any).foo = 1;
  • 将any断言为一个具体类型

type

类型别名

type常用于给联合类型起别名。

type s = string | number 
let a:s = '123'

字符串字面量类型

约束取值,只能取已定义的字符串的值。

type EventNames = 'click' | 'scroll' | 'mousemove';
function handleEvent(ele: Element, event: EventNames) {
    // do something
}

handleEvent(document.getElementById('hello'), 'scroll');  // 没问题
handleEvent(document.getElementById('world'), 'dblclick'); // 报错,event 不能为 'dblclick'

元组Tuple

合并不同类型的对象
定义一对值分别为 string 和 number 的元组:

let tom: [string, number] = ['Tom', 25];

当赋值或访问一个已知索引的元素时,会得到正确的类型:

let tom: [string, number];
tom[0] = 'Tom';
tom[1] = 25;

tom[0].slice(1);
tom[1].toFixed(2);

也可以只赋值其中一项:

let tom: [string, number];
tom[0] = 'Tom';

但是当直接对元组类型的变量进行初始化或者赋值的时候,需要提供所有元组类型中指定的项。

let tom: [string, number];
tom = ['Tom', 25];
let tom: [string, number];
tom = ['Tom'];

// Property '1' is missing in type '[string]' but required in type '[string, number]'.

枚举enum

可以手动赋值,无赋值缺省第一项为0。没有赋值的项在前一项基础上递增。

enum NumberType{
    one=1,
    two,
    three,
    four
}
console.log(NumberType);

转为js代码如下:

var NumberType;
(function (NumberType) {
    NumberType[NumberType["one"] = 1] = "one";
    NumberType[NumberType["two"] = 2] = "two";
    NumberType[NumberType["three"] = 3] = "three";
    NumberType[NumberType["four"] = 4] = "four";
})(NumberType || (NumberType = {}));
console.log(NumberType);
// {
//     '1': 'one',
//     '2': 'two',
//     '3': 'three',
//     '4': 'four',
//     one: 1,
//     two: 2,
//     three: 3,
//     four: 4
//   }

2种类型

  • 常数项
  • 计算所得项,后面不能接未赋值的项
enum Color{
    red,
    blue = 'blue'.length,
}
  • 常数枚举const enum:编辑阶段被删除,不包含计算成员。
  • 外部枚举declare enum:用于声明文件,定义的类型只会用于编译时检查。

属性和方法

参数得声明类型

class Person{
    name:string
    age:number
    constructor(name:string,age:number){
        this.name = name,
        this.age = age
    }
    sayHi(str:string){
        console.log('hi'+str);
        
    }
}

let  p1 = new Person('张三',18);
p1.sayHi('李四');

继承

class Animal{
    name:string
    age:number
    constructor(name:string,age:number){
        this.name= name
        this.age = age
    }
    sayHi1(str:string){
        console.log('hi,'+str);
    }
}

class Dog extends Animal{
    constructor(name:string,age:number){
        super(name,age)
    }
    sayHi1() {
    //可继承,也可重写
        console.log('狗狗的方法');
        super sayHi1('gougou');
    }
}

let h = new Dog('哈士奇',2);
h.sayHi1()

存取器

使用 getter 和 setter 可以改变属性的赋值和读取行为:

静态成员

加了static只属于类,实例对象获取不到。

public,private,protect

public 公用的,外部都可以获取
private 只能类内部用,不能被外界和子类访问,但子类可以继承。
protect 外部不能访问,但在子类内部可以。

readonly

只读。定义在参数上,创建并初始化参数。

抽象类

不允许实例化,是为了子类服务,类似接口。
在内部定义抽象属性和方法,不能有具体的实现,只能在子类具体实现。

类可以作为类型去定义

类与接口

类实现接口

一个类只能继承自另一个类,有时候不同类之间可以有一些共有的特性,这时候就可以把特性提取成接口(interfaces),用 implements 关键字来实现。

interface ISing{
    sing()
}
interface IDance{
    dance()
}

class P implements ISing,IDance{
    sing() {
        console.log("唱歌");
    }
    dance() {
        console.log("跳舞");
        
    }
}

接口继承接口

interface ISing{
    sing()
}
interface IDance{
    dance()
}

interface  IActivity extends ISing,IDance{

}

class P implements IActivity{
    sing() {
        console.log("唱歌");
    }
    dance() {
        console.log("跳舞");
        
    }
}

接口继承类

声明合并

函数合并

可以使用重载定义多个函数类型。

接口合并。

接口中的属性在合并时会合并到一个接口中,合并的属性的类型必须是唯一的

interface Cat{
    name:'小橘'
}
interface Cat{
    // name:'小橘1'//报错
    age: 2
}

const cat : Cat = {name:'小橘',age:2}

类的合并与接口合并规则一致。

泛型

定义时不确定类型,使用时才确定。

例子:定义一个函数,传入两个参数,一个是数据,一个是数量,生成对应数量的数据,保存在数组中。

function getArr<T>(value:T,count:number):T[]{
    const res : T[]= [];
    for(let i =0;i<count;i++){
        res.push(value);
    }
    return res;
}
console.log(getArr<number>(1,3));
console.log(getArr<string>('abc',3));

多个泛型参数

定义一个函数,实现[123,'123']->['123',123]

// 多个泛型参数
function reverse<T,U>(t:[T,U]):[U,T]{
    return [t[1],t[0]];
}

console.log(reverse<string,number>(['123',123]));
console.log(reverse<boolean,number>([true,123]));

泛型约束

由于事先不知道传入的参数是哪种类型,所以不能随意的操作它的属性或方法,这时,我们可以对泛型进行约束,只允许这个函数传入那些包含 length 属性的变量。
利用接口对泛型进行约束。

interface ILength{
    length:number
}

function getLength<T extends ILength>(x:T):number{
    return x.length
} 

console.log(getLength('123'));

泛型接口
可通过泛型接口去约束泛型函数

interface IArr<T>{
    (value:T,count:number):Array<T>
}

let getArr1:IArr<string> = function <T>(value:T,count:number):T[]{
    const res : T[]= [];
    for(let i =0;i<count;i++){
        res.push(value);
    }
    return res;
}

console.log(getArr1('ac',3));

可通过泛型接口去约束实例对象

interface IPerson<T>{
    name:T
}

let p2 : IPerson<string>={
    name:'abc'
}

let p3 : IPerson<number>={
    name:1
}

泛型类
泛型类与泛型接口类似,也是可以用于类的类型定义。

class Person<T>{
    name:string
    age:T
    constructor(name:string,age:T){
        name=this.name
        age=this.age
    }
}

const person = new Person<number>('abc',18);
const person2 = new Person<string>('abc','18');