TS从入门到出门

222 阅读9分钟

TS从入门到出门

工作流程

安装(ts)

全局安装:

npm install -g typescript

image-20220713185945256.png

编写(.ts文件)

image-20220713190301144.png

编译(tsc 文件名.ts)

tsc hello.ts

image-20220713190334132.png

会生成:

image-20220713190348031.png

运行(node 文件名.js)

node hello.js

image-20220713190407231.png

编译加运行

也可以将编译运行一起进行(使用ts-node):

安装:

npm install typescript ts-node @types/node@* -g 

image-20220713190514616.png

类型

类型注解

给变量添加类型约束。

数据类型

原有类型:

  • number

  • string

  • boolean

  • null

  • undefined

  • symbol

    let a:number = 9;
    let b:string = 'wdy';
    let c:boolean = true;
    let d:null = null;
    let e:undefined = undefined;
    let f:symbol = Symbol();
    console.log(a,b,c,d,e,f)   
    //9 wdy true null undefined  Symbol()
    
  • object

新增数据类型:

  • any

    如果不知道一个变量属于什么类型,就使用any,使用any后,它可以是任何类型:

    let a:any = 1
    let b:any = '我是string'          
    

    隐式any:声明变量不提供类型也不提供默认值或定义函数时,参数不指明类型:

image-20220714190039558.png 在函数不声明返回值时,也会将返回值隐式声明为any:

image-20220716235018621.png

在ts中过度使用any会使得ts失去意义,同时也会带来安全隐患。

  • unknown

    提到any就不得不说unknown了:unknown类型也可以代表任何类型,但是使用unknown做任何事情都是不合法的(unknown不允许定义的值有任何操作):

    unknown类型只允许赋值给any和unknown本身

image-20220716202257954.png

  • 数组

    两种格式:变量名:类型名[] = [] 变量名:Array<类型名> = []

    数组里面的类型必须提前说明,该数组只能存储规定类型数据:

    let array1:number[] = [1,2,3,4,5];
    array1.push(6);
    //❌
    array1.push('wdy');
    let array2:(number|string)[] = [1,2,3,'木头人'];
    array2.push(6);
    array2.push('wdy');
    ​
    let array3:Array<number> = [1,2,3,4,5];
    array3.push(6);
    //❌
    array3.push('wdy');
    let array4:Array<number|string> = [1,2,3,'木头人'];
    array4.push(6);
    array4.push('wdy');
    ​
    console.log(array1);
    console.log(array2);
    console.log(array3);
    console.log(array4);
    

image-20220719175038858.png

  • 元组(Tuple)

    可以将元组理解为限制数据类型与个数的数组:

    let tup:[number,number,number,string] = [1,2,3,'木头人'];
    tup.push(1);
    tup.push('wdy');
    console.log(tup)
    
  • void

    空类型,一般用于函数返回值。当函数没有返回值时或者只有return时使用:

    function my():void{
        return 
    }
    function myy():void{
        console.log('wdy')
    }
    
  • 枚举(Enum)

    用于定义一些带有名字的常量,来描述特定的场景:

    枚举名开头首字母大写

    enum 枚举名{

    常量名,

    常量名

    }

    enum Week{
        Monday,
        Tuesday,
        Wednesday,
        Thursday,
        Friday,
        Saturday,
        Sunday  
    }
    ​
    //数字递增
    console.log(Week.Monday);
    console.log(Week.Tuesday);
    console.log(Week.Wednesday);
    ​
    //反向映射
    console.log(Week[0]);
    console.log(Week[1]);
    console.log(Week[2]);
    

image-20220719182410667.png

如果给元素赋初值:

```ts
enum Week{
    Monday = 10,
    Tuesday,
    Wednesday,
    Thursday,
    Friday,
    Saturday,
    Sunday  
}
//数字递增
console.log(Week.Monday);console.log(Week.Tuesday);console.log(Week.Wednesday);
​
//反向映射
console.log(Week[0]);console.log(Week[1]);console.log(Week[2]);
​
enum Weekk{
    Monday = '周一',
    Tuesday = '周二',
    Wednesday ='周三',
    Thursday = '周四',
    Friday = '周五',
    Saturday = '周六',
    Sunday = '周日'  
}
console.log(Weekk.Friday);console.log(Weekk.Saturday);console.log(Weekk.Sunday);
​
enum Weekkk{
    Monday = 10,
    Tuesday,
    Wednesday,
    Thursday = '周四',
    Friday = '周五',
    Saturday = '周六',
    Sunday = '周日'  
}
console.log(Weekkk.Monday);console.log(Weekkk.Thursday);
```

![image-20220719183827808]()
  • never

    用户无法到达的类型:抛出异常,死循环等

联合类型

一个变量只能是一种数据类型吗?并不是——>联合类型:

image-20220716202657545.png

联合类型代表我们可以将个变量设定为多种数据类型,这些数据类型之间使用|隔开。

交叉类型

将多个类型叠加使其成为一种新的类型,当我们使用该类型时,需要将多个类型全部实现:

type 类型名 = 类型&类型&类型

当出现同名参数时,如果类型相同则合并,如果类型不同则报错,

type My = {name:string} & {age:number};
let my:My = {
    name:'wdy',
    age:9
}
​
type Name = {name:string}
type Age = {age:string}
type Myy = Name & Age;
let myy:Myy = {
    name:'wdy',
    age:'9'
}
​
//❌
//age为number类型和string类型,判定认为age不能同时是number和string,所以会认为是never类型而报错。
type Boom = My & Myy;
let boom:Boom = {
    name:'wdy',
    age:9,
}

image-20220720115214395.png

字面量类型

字面量不仅可以表示值,还可以表示类型即所谓的字面量类型:

当我们使用字面量类型时,变量的值和类型将不能在改变(与字面量类型保持一致),但可以将该变量赋给其他值:

//字符字面量
let a:'my' = 'my';
//数字字面量
let b:9 = 9;
//布尔字面量
let c:true = true;
​
let aa:string;
let bb:number;
let cc:boolean;
​
//❌
a = aa;
aa = a;
//❌
b = bb;
bb = b;
//❌
c = cc;
cc = c;
​

我们一般使用字面量类型来对值进行限制,这样可以使我们对于一些参数定义的更加具体:

type Name = 'wdy'|'WDY';
function my(name:Name,age:number):void{
    console.log(`${name}${age}`);
}
my('wdy',9);
my('WDY',9);
my('WdY',9)
​

image-20220720111435318.png

类型别名

别名即 (别的名字) 我们可以使用类型别名将类型名换成我们想要使用的名字:

格式:type 别名 = 类型

image-20220716225450626.png

函数

声明

//函数声明
function add(a:number,b:number):number{
    return a+b
}
console.log(add(10,10))
​
//函数表达式
const adding = function(a:number,b:number):number{
    return a+b
} 
console.log(adding(10,10))
​
//箭头函数
const my = ():string => {
    return 'wdy'
}
console.log(my())
​
//无返回值
const mmy = ():void =>{
    console.log('wdy')
}
mmy()

image-20220716235828277.png

函数模板/函数别名

有时候我们需要声明较多结构相同的函数,这时候我们可以使用函数模板:

type 模板名 = (参数:参数类型) => 返回值类型

//定义函数模板,后续使用可以直接套用
type Add = (a:number,b:number) => number
const add : Add = (a,b)=>{
    return a+b
}
console.log(add(10,10))
​

image-20220717000734221.png

可选参数

可选参数即使用函数时,该参数可传可不传:

可选参数只能声明在必须参数后面

参数名?:数据类型

//可选参数,在参数名后面加?
function my(name:string,age?:number|string):void{
    console.log(`${name} + ${age}`)
}
my('wdy',9);
my('wdy');
​
//❌
//可选参数只能声明在必须参数之后
function myy(name?:string,age:number|string):void{
    console.log(`${name} + ${age}`)
}

image-20220718102933348.png

默认值

带有默认值的参数也应该声明在必须参数后面

参数名:参数类型 = 默认值

function my(name:string,age:number|string = 9):void{
    console.log(`${name} + ${age}`)
}
my('wdy',99);
my('wdy')
​
//❌
function myy(name:string = 'wdy',age:number|string):void{
    console.log(`${name} + ${age}`)
}
myy('wdy',99);
myy(99)

image-20220718103802322.png

对象

声明

对象中的属性在声明时,可以将属性设置为可选参数

const 对象名 :{

属性名1:属性类型,

属性名2?:属性类型,

方法名1(参数名:参数类型):返回值类型,

方法名2?:(参数名:参数类型)=>返回值类型

}={

属性名1:值1,

方法名1:方法体1

}

const my:{
    name:string,
    age?:number|string,
    sayHi():string,
    sayHello?:()=>void
}={
    name:'wdy',
    sayHi(){return 'Hello'}
}
console.log(my.name);
console.log(my.age);
console.log(my.sayHi());
console.log(my.sayHello())
​

image-20220718105956606.png

对象模板/对象别名

type 模板名 = {

属性1:属性

属性2:属性

方法1:方法

}

type My = {
    name:string,
    age?:number|string,
    sayHi():string,
    sayHello?:()=>void
}
let my:My = {
    name:'wdy',
    age:9,
    sayHi(){
        return 'Hello'
    }
}
console.log(my.name),
console.log(my.age),
console.log(my.sayHi())

image-20220718110537565.png

接口

在面向对象的语言中,接口是一个十分重要的概念,它是对行为的抽象(类似于继承但有不完全等于继承):

interface 接口名{

属性:属性类型,

}

使用接口定义变量时,变量格式必须与接口格式相同,属性不能多也不能少:

interface My{
    name:string,
    age:number|string
}
​
let my:My = {
    name:'wdy',
    age:9
}
​
//❌
let myy:My = {
    name:'wdy'
}
//❌
let myyy:My = {
    name:'wdy',
    age:9,
    hobby:'sleep'
}
可选属性

那么,当我们对于一些属性的必要性不确定时应该怎么写呢?:

interface 接口名{

属性名?:属性类型

}

interface My{
    name:string,
    age?:number|string
}
​
let my:My = {
    name:'wdy'
}
​
let my:My = {
    name:'wdy',
    age:9
}
任意属性

上面的两种方式我们只能按照接口的属性格式定义属性,如果我们想要新加属性呢?:

interface 接口名{

[propName:类型]:属性类型

}

一旦定义任意属性,其余确定属性和可选属性的类型必须是他的子集:

//❌
interface My{
    name:string,
    age?:number,
    [propName:string]:string
}
​
interface Myy{
    name:string,
    age:number,
    [propName:string]:string|number
}
let myy:Myy = {
    name:'wdy',
    age:9,
    hobby:'sleep',
    sex:0
}
​
只读类型

当我们希望一些类型在初始赋值后就不能被改变时,我们可以使用只读类型:

interface 接口名{

readonly:属性值:属性类型

}

interface My{
    readonly name:string,
    age:number,
}
​
let my:My = {
    name:'wdy',
    age:9
}
my.age = 99;
//❌
my.name = 'WDY'

image-20220720175634429.png

类型推断

当我们不进行类型注解时,ts会根据上下文进行推断,从而得到变量类型,即在一些时候,我们可以不主动声明数据类型。

  • 当我们声明一个变量,且赋予初始值时,该变量会自动推断其类型
  • 当我们声明一个变量,未赋予初始值时,该变量会被推断为any类型,这也成为隐式any
  • 当我们声明一个函数,未赋予初始值时,该函数如果没有返回值,会被推断为any,如果有返回值,会自动推断其返回值类型
let a = 123
let b = '123'
let c
c = 123;
c = '123'
console.log(typeof a)
console.log(typeof b)
​
function my(){
    console.log('wdy')
}
function myy(name:string,age:number){
    return `${name} ${age}`
}
my();
console.log(myy('wdy',9))

image-20220719185724954.png

类型断言

当我们非常了解某个值时,我们可以使用类型断言,使用类型断言后,ts会按我们断言的类型通过编译:

有两种方式,在tsx中必须使用第二种:

<类型>变量名

变量名 as 类型

let a:string = 'wdy';
let lang:number = a.lengthlet b:string = 'wdy';
let langg:number = (<string>b).lengthlet c:string = 'wdy';
let langgg:number = (b as string).lengthconsole.log(lang)
console.log(langg)
console.log(langgg)

image-20220720105323841.png

当我们在使用接口声明变量时,可以使用类型断言避开检验:

interface My{
    name:string,
    age:number
}
​
//❌,没有根据接口的数据类型声明
let my:My = {
    
}
​
//使用类型断言避开检查
let myy:My = {
​
} as My

image-20220720200503287.png

泛型

声明

在定义函数,接口或类时,不预先指定具体的类型,而是在使用的时候在指定类型:

function my<T>(name:T):T{
    return name
}
console.log(my<string>('wdy'))
console.log(my('wdy'))
console.log(my<number>(123))
console.log(my(123))
​
function myy<T,U>(name:T,age:U):string{
    return `${name} ${age}`
}
console.log(myy<string,number>('wdy',9));
console.log(myy<string,number|string>('wdy','9'))

image-20220721113817009.png

泛型约束

在函数内部使用变量时,没有提前确定变量类型,因此我们不能直接使用其方法,这时,我们可以提前对泛型进行约束:

interface My{
    length:number
}
​
//❌
function my<T>(name:T):T{
    console.log(name.length)
    return name
}
​
function myy<T extends My>(name:T):T{
    console.log(name.length)
    return name
}
console.log(myy<string>('wdy'))
//❌
console.log(myy<number>(99))

image-20220721115002513.png

泛型接口

在泛型中也存在接口:

interface 接口名{

要求格式

}

interface Length{
    length:number
}
​
interface Name{
    <T extends Length>(name:T):T
}
​
let my:Name;
my = function<T extends Length>(name:T):T{
    console.log(name.length)
    return name
}
console.log(my('wdy')) 
​
interface Namee<T>{
    (name:T):T
}
let myy:Namee<any>;
myy = function<T>(name:T):T{
    return name;
}
console.log(myy('wdy'))
​

image-20220721175758079.png

声明

class My{
    name:string;
    age:number;
    constructor(name:string,age:number){
        this.name = name;
        this.age = age
    }
    sayHello():void{
        console.log('Hi')
    }
}
​
let my = new My('wdy',9);
my.sayHello()

image-20220722112312358.png

继承

继承类需要使用extends

子类声明构造函数需要在constructor内使用super

class My{
    name:string;
    age:number;
    constructor(name:string,age:number){
        this.name = name;
        this.age = age
    }
    sayHello():void{
        console.log('Hi')
    }
}
​
class Myy extends My{
    hobby:string
    constructor(name:string,age:number,hobby:string){
        super(name,age)
        this.hobby = hobby
    }
    sayHi():void{
        console.log('Hello')
    }
}
let myy = new Myy('wdy',9,'sleep');
myy.sayHello();

image-20220722114337010.png

修饰

在TS的类中共有三种修饰符:

修饰符作用
public默认使用,属性或方法为共有,所有地方均可访问
private属性或方法为私有,不能在类外访问
protected属性或方法受到保护,子类可以访问
class My{
    public name:string;
    private hobby:string;
    protected age:number;
    constructor(name:string,hobby:string,age:number){
        this.name = name;
        this.hobby = hobby;
        this.age = age
    }
    private sayHobby():void{
        console.log(this.hobby)
    }
}
​
class Myy extends My{
    constructor(name:string,hobby:string,age:number){
        super(name,hobby,age)
        //❌
        console.log(this.hobby);
        console.log(this.age)
    }
}
​
let my = new My('wdy','sleep',9);
​

当我们类的构造函数也可以使用修饰符修饰:

修饰符作用
public默认
private该类不允许被继承,也不允许被实例化
protected该类只允许被继承,不允许被实例化