• 有问题请联系QQ:2374146085
  • 有问题请联系QQ:2374146085

TypeScript初识

1年前 (2022-01-24) 1121次浏览 已收录 0个评论 扫描二维码

导读

类型声明、函数返回值类型声明、基本类型、联合类型、字面量、unknow未知类型、object类型、Array类型、tuple元组、类型别名、编译选项、enum枚举、构造函数constrctor、继承 、接口、抽象类、属性的封装、泛型。函数的重载、类型断言as、非空断言!、可选参数?、可选链?.、??空值合并操作符、never类型


类型

类型声明

变量声明

//声明一个变量a,同时指定它的类型为number
let a:number;
a=10;
a='wxl' //会报错,因为a在使用的过程中值只能是数字

//声明完变量直接进行赋值
let a:number = 10;

//当然,变量的声明和赋值可以同时进行,然后ts可以自动对变量进行类型检测,
//后续这个变量所有的赋值操作都是基于第一次类型检测来进行的。
let c = true;
c = false;

函数类型声明

//接收数字类型的参数
//函数的返回类型是number
function sum(a:number,b:number) :number{
    return a+b;
}

let result = sum(1,2)


//设置函数结构的类型声明
let d:(a:number,b:number)=>number

let m:()=>void //void在这里代表是任意类型

当函数有默认值的时候,尽量放在最后。

函数传值时建议顺序:(必传参数,有默认值的参数,可选参数)

//会报错
function fn(x:number=10,y:number){
}
// fn(20) 报错
fn(undefined,20) //不会报错

//不会报错
function fn2(x:number,y:number=10){
}
fn2(20)

函数的重载

名称相同,但参数或参数类型不同,就是函数重载。

// 定义函数
function add(num1:number,num2:number):number; //还没有函数体实现,只是先定义
function add(num1:string,num2:string):string;
function add(num1:string,num2:number):string;
function add(num1:number,num2:string):string;

//函数实现
function add(num1:any,num2:any):any{
    return num1+num2
}
console.log(add(1,2)) //3
console.log(add('a','b')) //ab
console.log(add('a',1))//a1
console.log(add(1,'a'))//1a

基本类型

联合类型

let c: boolean | string;
c=true
c='wxl'
//联合类型经常和type一起使用
//type a: string | number

联合类型在代码提示上是全部类型的整合,不是联合类型中其中某一个,比如上边代码中的c的类型就是’boolean | string’而不是单一的boolean或者string。

函数重载和联合类型都能解决函数参数不确定的需求,但是优先使用联合类型,只有在返回值不确定的情况下更适合函数重载。

字面量

const message='zzl'
//检测后,相当于 const message:'zzl'='zzl'

let message2 = 'wxl'
//检测到message2的类型却是string

字面量的意义就是必须结合联合类型

let b: 2 | 3;
b=2; 
b=3; 
b=100;//此时会报错,b只能在2和3之间赋值

null、undefined类型

let n:null=null //null类型只有一个值,就是null
let n2:undefined=undefined

unknow未知类型

//表示未知类型的值(即可以是任意类型)
let e:unknown;
e=10;
e='wxl'

let y:string;

y=e;//此时会报错,虽然都是string类型,但是e定义的类型是unknow,
    //但是如果用相同效果但不安全的any类型,此时就不会报错了
    //因为,unknow类型只能 赋值给 any和unknown类型,防止滥用
    //any类型可以赋值给任意类型

//解决办法
//方法1
if(typeof e === 'string'){
    y=e; //此时就不会报错了,因为这里使用了类型保护
}
//方法2 (类型断言)
y = e as string
y = <string> e;

常见的类型保护

  • typeof
  • 平等缩小(比如===、!==)
  • instanceof 是否是它的实例
  • in
  • 等等

in的使用

type Fish={
    swim:()=>void
}
type Dog={
    run:()=>void
}

function test(animal:Fish|Dog){
    if('swim' in animal){
        animal.swim()
    }else{
        animal.run()
    }
}
const  f:Fish={
    swim(){
        console.log('鱼在游')
    }
}
test(f)

类型断言as

const el = document.getElementById('d') as HTMLImageElement
const el2 = document.querySelector('img') //自动HTMLImageElement类型
//只有HTMLImageElement类型的变量才能取到img的src属性
el.src='。。。'

非空断言!

跳过ts在编译阶段对变量的检测。非空断言不能滥用,不安全,建议使用可选链。

function print(message?:string){
    // console.log(message.length) 会报错

    // if(message){
    //     console.log(message.length)
    // }
    
    console.log(message.length!) // ! 表示我肯定message有值
}

print('aaa')

可选参数?

function test2(a:number,b?:string){}
//可选参数要放在必选类型后边
//属性名后加?,表示这个属性是可选的,它相当于 a:undefined | number 即联合类型

可选链?. (es11的特性)

它的作用是当对象的属性不存在时,会短路,即直接返回undefined;如果存在,那么才会继续执行。

type person={
    name:string,
    friend?:{
        name:string,
        age?:number
    }
}
const info:person={
    name:'zzl',
    friend:{
        name:'wxl'
    }
}
console.log(info.name) //zzl
// friend存在的话就去拿它的name,不存在就返回undefined
console.log(info.friend?.name) //wxl
console.log(info.friend?.age) //undefined 因为拿第一层拿不到的时候返回就是undefined,只有undefined.age才是报错的原因

const info2:person={
    name:'zzl',
}
console.log(info2.friend?.name) //undefined

??空值合并操作符

let message:string|null = null
const content = message ?? '我是默认值'
console.log(content) //我是默认值 ,因为message没有值

let message2:string|null = 'wxl'
const content2 = message2 ?? '我是默认值'
console.log(content2) //wxl

object类型

//属性名后加?,表示这个属性是可选的,它相当于 a:undefined | number 联合类型
let p:{name:string, age?:number}
p = {name:'wxl'}

[propName:string]:any   表示任意类型的属性,即必须有一个name属性,其他属性可有可无 
let c:{name:string, [propName:string]:any};
c = {name:'wxl',age:18,gen:'男'};

Array类型

let g:number[] //推荐写法

let g:Array<number>; //react jsx中有冲突,<div></div>
g = [1,2,3];

tuple元组

//元组就是多种元素的组合,且是固定长度的数组
let h: [string,number,number];
h = ['w',1,2];

enum枚举

enum Gender{
    man=0,
    gril=1
}
let i:{name:string,gender:Gender};
i={
    name:'wx',
    gender:Gender.man
}
console.log(i.gender === Gender.man)

never类型

//当一个函数压根都没有返回值,此时就可以使用never 
function test():never{
     throw new Error()
 }

.d.ts声明类型

不运行,单纯用做声明类型的

index.d.ts

//因为有时候导入的模块,作者自己没有写.d.ts,所以会报错,需要我们自己定义.d.ts,并声明它的类型)
//所以需要使用,声明模块
declare module 'lodash' {
    export function join(arr: any[]): void
}

// 声明变量/函数/类
declare let zzlName: string
declare function getName(): void
declare class Person{
    name:string
    age:number
    constructor(name:string,age:number)
}

//声明文件
declare module '*.jpg'

//声明命名空间
declare namespace $ {
    export function ajax(url:string):string
}

ts文件根据index.d.ts的类型声明来进行类型检测。,是会运行的(编译成js)

test.ts

import a from './a1.jpg'

const p = new Person("why",19)
console.log(p.name) 

$.ajax('127.0.0.1')

类型别名

type myType = boolean | string;
let k:myType;
k='d'

编译选项

在项目目录下创建一个tsconfig.json的文件,并在目录路径下的终端里运行 tsc命令,此时目录 下所有的ts文件都会编译成Js文件。

{

    //所有严格检查的总开关
    "strict":true,

    // include用来指定哪些文件可以被编译 **表示任意目录  *表示任意文件
    "include": [
        "./src/**/*"
    ],

    // 定义需要排除在外的目录
    // 默认值:["node_modules"......]
    "exclude": [
        
    ],


    "compilerOptions": {
        "target": "ES6"
    },

    //用来指定编译后的js文件所在目录
    "outDir":"./dist",

    //是否对js文件进行编译,这样也会输出到之前设置的目录里
    "allowJss":true,

    //是否检查js代码是否符合ts规范
    "checkJs":true,

    //是否移除注释
    "removeComments":true,
    
    //当有错误的时候,就不生成编译文件
    "noEmitOnError":true,


} 

class

构造函数constrctor

class Dog{
    name:string;
    constructor(name:string){
        // 在构造函数中this就说是当前新建的那个对象
        this.name=name;
    }
    say(){
        // this是当前调用方法的对象,即表示当前对象
        console.log(this)
    }
}
const dog = new Dog('ww');
const dog2 = new Dog('xx');
console.log(dog)
console.log(dog2)
dog.say();

继承

class Animal{
    name:string;
    constructor(name:string){
        this.name=name;
    }
    sayHello(){
        console.log('我叫了 ')
    }
}

class Dog extends Animal{
}
class Cat extends Animal{
    run(){
        console.log('猫在跑')
    }
    sayHello(){
        console.log('我要独特的叫')
    }
}

const dog = new Dog('旺财');
const cat = new Cat('加菲猫')

dog.sayHello() //我叫了
cat.run()      //猫在跑
cat.sayHello() //我要独特的叫

super 父类

class Animal{
    name:string;
    constructor(name:string){
        this.name=name;
    }
    sayHello(){
        console.log('我叫了 ')
    }
}


class Cat extends Animal{

    age:number;

    //当重写父类的构造函数时,
    constructor(name:string,age:number){
        //调用父类的构造函数,此时就有name这个属性了。
        super(name)
        this.age=age
    }
    run(){
        console.log('猫在跑')
    }
    //重写sayHello
    sayHello(){
        console.log('我要独特的叫')
        //当然也可以继续调用父类的方法,super.sayHello()
    }
}

const cat = new Cat('加菲猫',12)

cat.run() 
cat.sayHello()

访问器setter与getter

class Person{
    private _name:string
    constructor(name:string){
        this._name=name
    }
    
    set name(newName){
        this._name=newName
    }
    get name(){
        return this._name
    }
}
const p = new Person('zzl')
p.name='wxl'
console.log(p.name)

抽象类

  • 抽象类不能用来创建对象
  • 抽象类就是专门用来 被继承的类
abstract class Animal{
    name:string;
    constructor(name:string){
        this.name=name
    }
    
    //定一个抽象方法 
    abstract sayHello():void
}


class Cat extends Animal{
    sayHello(){
        console.log('我要独特的叫',this)
    }
}

const cat = new Cat('加菲猫')
cat.sayHello()

接口

  • 接口用来定义一个类结构,用来 定义一个类中应该包含哪些属性和方法
  • 同时 接口也可以当成type x 类型别名声明来使用。
  • 接口中所有的属性都不能有实际的值
  • 接口中所有的方法都是抽象方法
interface  myInterface<T=string>{ //泛型给一个默认值
    name:string;
    sayHello():void;
    action?:(value:T)=>T
}
//等同于 type myInterface {name:string,age:number}


// 定义类时,可以使类去实现 一个接口 
class A implements myInterface{
    name: string;
    constructor(name:string){
        this.name=name
    }
    sayHello(): void {
        
    }
}

const a = new A('wxl')

索引类型

// 通过interface来定义索引类型
interface IndexTest{
    [index:number]:string
}
const a:IndexTest={
    0:'zzl',
    1:'wxl'
}

interface IndexTest2{
    [name:string]:number
}
const a2:IndexTest2={
    'age':1,
    'height':150
}

属性的封装

  • public. 修饰的属性可以在任意位置访问和修改
  • private 私有属性,只能在类的内部进行访问(修改)
    • 可以通过添加 方法使得私有属性可以被外部访问
  • protected 受保护的属性,只能在当前类和当前类的子类中访问(修改)
  • readonly 只读属性,可以在构造器中赋值,且赋值之后就不可以修改了。
    • 属性本身不能进行修改,但是如果它是对象类型,则对象中的属性是可以修改的
class Person{
    private _name:string;
    private _age:number
    
    constructor(name:string,age:number){
        this._name=name
        this._age=age
    }

    get name(){
        return this._name
    }
    set name(value:string){
        //可以判断将要赋值是否符合要求
        if(value.length <= 3){
            this._name=value
        }else{
            console.log('名字太长,不允许修改')
        }
    }

    get age(){
        return this._age
    }
    set age(value:number){
        this._age=value
    }
}

const per = new Person('wxl',10)
// per._name='ll';//不允许这样访问,会报错 
per.name='hhh'
console.log(per.name)

per.age=100
console.log(per.age)



///////////////////////////

//也可以直接将属性定义在构造函数中,和上面类的构造函数一样
class C{
    constructor(public name:string,public age:number){}
}

const c = new C('xxx',10)
console.log(c)

泛型

在定义函数或类时,如果遇到类型不明确就可以使用泛型,即类型的参数化,让外界决定类型。

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

//可以直接调用具有泛型的函数
fn<number>(10); //此时T就是number
fn<string>('wxl'); //此时T就是String


////////////////

interface Inter{
    //表示对象里要有length属性
    length:number
}

//interface I2<T1,T2=string>{
//    name:T1,
//    age:T2
//}

//T extends Inter 表示传的泛型必须是Inter的实现类,即实现了类型的类型约束
function fn2< T extends Inter>(a: T):number{
    return a.length
}

// 'wxl'有length属性
fn2('wxl')

// fn2(10) //此时会报错,因为10没有length属性

编译环境

node下的编译环境

npm install ts-node -g
npm install tslib @types/node -g
ts-node index.ts #此时就可以编译了

 

 


渣渣龙, 版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权
转载请注明原文链接:TypeScript初识
喜欢 (2)

您必须 登录 才能发表评论!