typeScript基本使用

142 阅读7分钟

typeScript基本使用

ts中的数据类型

同js的数据类型: undefined、Nul、boolean、string、number、Symbol、Object、Array、Function

其他数据类型:void、any、never、元组、枚举、接口、泛型、高级类型

基本类型

//boolean
let bool: boolean = true
//number
let num: number = 1
//string
let str: string = 'abc'
//undefined
let unde: undefined = undefined
//nul
let nul: null = null
// symbol
let sy: symbol = Symbol()
//多类型
let abc: boolean | string | number | undefined | null = 123

数组

//Array
let arr1: number[] = [1, 2, 3]
let arr2: Array<number> = [1, 2, 3]
let arr3: Array<number | string> = [1, 2, 3, "4"]

元组

元组表示一个确定长度和类型的数组

元组可以通过使用数组方法(比如push)改变值,但是访问时不能超出其规定的界限

// 元组 
let tuple: [number, string]   //指定tuple是一个长度为2并且第一个数据是number第二个数据是string的数组
tuple = [0, '1']  //赋值成功
tuple[2]=1 //报错
tuple.push(2)  //给元组增加一项
console.log(tuple) //[0,'1',2]
tuple[2] //报错

函数

// 函数 
// 声明接受两个number类型的参数并且函数返回类型为number
// 函数返回类型可以不声明,ts会自动推导
let fn = (x: number, y: number): number => x + y 

//定义一个函数类型
let fnType: (x: number, y: number) => number
fnType=(a, b) => a + b //赋值的时候不用在声明类型

对象

// 对象

// 直接声明object类型
let obj: object = { x: 1, y: 2 }
obj.x=11 //报错 类型“object”上不存在属性“x”。

// 声明object的属性及属性对应的类型
let obj1: { x: number; y: number } = { x: 1, y: 2 }
obj1.x=11

void

void表示没有任何类型,通常用于函数表名函数没有返回值

// void
let fn: () => void
let fn1 = () => {}

any

any表示任意类型,可以理解为没有指定类型

// any
let num: any = 4
num='4' //ok

never

never类型表示的是那些永不存在的值的类型

// 返回never的函数必须存在无法达到的终点
let error = (): never => {
    throw new Error("error")
}
let endless = () => {
    while (true) {}
}

枚举(enum)

枚举类型是对JavaScript标准数据类型的一个补充

数字枚举

// 数字枚举
// 默认从0开始依次递增
enum enumA {
    a,
    b,
    c,
    d,
}
console.log(enumA.a) // 0
// 使用枚举值
let c: enumA = enumA.c   //2


// 指定开始值
enum enumB {
    a=100,
    b,
    c,
    d,
}
console.log(enumB.a) // 101
console.log(enumB) //{100: "a", 101: "b", 102: "c", 103: "d", a: 100, b: 101, c: 102, d: 103}
console.log(enumB[100]) //a

从打印结果可以看出,数字枚举键和值可以反向映射,其实现如下:

var enumB;
(function (enumB) {
    enumB[enumB["a"] = 100] = "a";
    enumB[enumB["b"] = 101] = "b";
    enumB[enumB["c"] = 102] = "c";
    enumB[enumB["d"] = 103] = "d";
})(enumB || (enumB = {}));

字符串枚举

字符串枚举不支持反向映射

enum enumC {
    a = "a",
    b = "b",
}
console.log(enumC) //{a: "a", b: "b"}

异构枚举

字符串枚举和数字枚举混用就是异构枚举

enum enumD {
    a,
    b = enumC.a,
    c = 1 + 3,
    // computed member
    d = Math.random(),
    e = '123'.length,
    f = 4,
  	g
}

// 编译一下
var enumD;
(function (enumD) {
    enumD[enumD["a"] = 0] = "a";
    enumD[enumD["b"] = enumC.a] = "b";
    enumD[enumD["c"] = 4] = "c";
    // computed member
    enumD[enumD["d"] = Math.random()] = "d";
    enumD[enumD["e"] = '123'.length] = "e";
    enumD[enumD["f"] = 4] = "f";
    enumD[enumD["g"] = 5] = "g";
})(enumD || (enumD = {}));

通过编译后的代码可以看出,当枚举的值是表达式的时候不会立马去计算表达式的值

常量枚举

用const声明的枚举就是一个常量枚举,常量枚举编译之后不会产生任何代码

const enum enumE {
    a,
    b,
    c,
}
let a: enumE = enumE.a

// 编译后
let a = 0 /* a */;

接口(interface)

接口可以理解为规范或者约束,预先把对象的结构定义好

对象类型接口

interface List {
    name: string
    readonly id: number   // readonly标明此属性为只读属性不能被修改
    age?: number  //?标明此属性可以有也可以没有
    [x: string]: any  //标明允许对象可以有其他任意数量的属性
}

函数类型接口

interface Add {
    (x: number, y: number): number
}
// 和函数声明是一样的
let Add1: (x: number, y: number) => number
// 也可以使用类型别名
type Add2 = (x: number, y: number) => number
let add: Add = (a, b) => a + b

混合型接口

interface Lib {
    (): void //标明lib是一个没有返回值的函数
    version: string
    doSomething(): void
}
function getLib() {
    let lib = (() => {}) as Lib  //用as进行类型断言
    lib.version = "1.0.0"
    lib.doSomething = () => {}
    return lib
}
let lib1 = getLib()
lib1()
lib1.doSomething()
    

基本使用

class Dog {
    // private constructor(name: string) {   给构造函数加上private此类不能被继承不能被实例化
    //    this.name = name
    // }
  
   // protected constructor(name: string) { 给构造函数加上protected此类不能被实例化只能被继承
   //     this.name = name
   //  }
  
  
    constructor(name: string) {
        this.name = name
    }
    public name: string = "dog"  //public 默认修饰符 不写就是public
    private age: number = 10  //private 私有修饰符 只能被类本省调用不能被实例调用
    protected eat() {}  //protected 受保护修饰符 受保护的变量只能在本类和子类中使用,不能在实例中使用
    readonly sex: string = "1" //readonly只读属性,不能被修改
    static food: string = "bones" //static静态修饰符 只能通过类来访问不能被实例访问,可以被继承
    run() {}
    sleep() {
        console.log("Dog sleep")
    }
}
let dog = new Dog("tom")
dog.age //报错 属性“age”为私有属性,只能在类“Dog”中访问
dog.eat() //报错 属性“eat”受保护,只能在类“Dog”及其子类中访问
dog.food //

// 编译后
class Dog {
    constructor(name) {
        this.name = "dog";
        this.age = 10;
        this.sex = "1";
        this.name = name;
    }
    eat() { }
    run() { }
    sleep() {
        console.log("Dog sleep");
    }
}
Dog.food = "bones";

抽象类

抽象类只能被继承,不能创建实例

abstract class Animal {
    eat() {
        console.log('eat')
    }
    abstract sleep(): void   //定义抽象方法,具体方法内容由子类去实现
}


class Dog extends Animal {
    sleep() {
        console.log('Dog sleep')
    }
}

let dog = new Dog()
dog.eat()
dog.sleep()

类和接口的关系

接口是规范,可以用接口的规范实现一个类

接口只能约束类的公有成员

接口也可以被继承

接口可以继承类

interface Human {
    name: string;
    eat(): void;
}

class Asian implements Human {
    constructor(name: string) {
        this.name = name;
    }
    // private name: string   接口上的属性必须为public
    name: string
    eat() {}
    age: number = 0
    sleep() {}
}


//接口Man继承Human 并增加一个自己的方法
interface Man extends Human {
    run(): void
}
interface Child {
    cry(): void
}
 //接口Boy继承Man和Child 取并集 boy上有4个属性
interface Boy extends Man, Child {}
let boy: Boy = {
    name: '',
    run() {},
    eat() {},
    cry() {}
}


class Auto {
    state = 1
}
interface AutoInterface extends Auto {}
//Bus继承于Auto已经有state属性
class Bus extends Auto implements AutoInterface {
}

泛型

泛型不预先确定数据类型,具体的数据类型在使用的时候确认

// 泛型函数
function fn<T>(value: T): T {
    console.log(value);
    return value;
}
fn<string[]>(['a', ',b', 'c'])   //使用的时候声明类型
fn(['a', ',b', 'c'])  //使用类型推导

// 声明一个泛型函数
type Fn = <T>(value: T) => T

// 泛型接口
interface Inter<T> {
    (value: T): T
}
let myInter: Inter<number> = fn  //使用泛型接口需要声明类型
myInter(1)

// 泛型类
class C<T> {
    run(value: T) {
        console.log(value)
        return value
    }
}

泛型不能用于类的静态属性

泛型约束

不同类型具有不同的属性,使用类型下的属性是必须要加上类型约束

interface Length {
    length: number
}
function logLength<T extends Length>(value: T): T {
    console.log(value, value.length);
    return value;
}
logLength('12') 
logLength({ length: 3 })
logLength(12) //报错 12不存在length属性

类型断言

告诉编译器我确定这个数据的类型,听我的,你不要报错~~

interface Foo {
    bar: number
}
let foo = {} as Foo  // as 语法
let foo = <Foo>{}  // <>语法

let someValue: any = "this is a string"
let strLength: number = (someValue as string).length // as 语法
let strLength1: number = (<string>someValue).length // <>语法

类型保护

enum Type { Strong, Week }

class Java {
    helloJava() {
        console.log('Hello Java')
    }
    java: any
}

class JavaScript {
    helloJavaScript() {
        console.log('Hello JavaScript')
    }
    js: any
}

// 函数守卫
function isJava(lang: Java | JavaScript): lang is Java {
    return (lang as Java).helloJava !== undefined
}


function getLanguage(type: Type, x: string | number) {){
  let lang = type === Type.Strong ? new Java() : new JavaScript()
  // 不使用类型保护 一直使用类型断言
    if ((lang as Java).helloJava) {
         (lang as Java).helloJava();
     } else {
         (lang as JavaScript).helloJavaScript();
    }
  
   // 用instanceof判断是哪个实例
     if (lang instanceof Java) {
         lang.helloJava()
     } else {
         lang.helloJavaScript()
     }
		
  	// 用in判断属性
    if ('java' in lang) {
        lang.helloJava()
    } else {
        lang.helloJavaScript()
    }
  
   // 用typeof判断
    if (typeof x === 'string') {
        console.log(x.length)
    } else {
        console.log(x.toFixed(2))
    }
  
   // 用自定义函数
    if (isJava(lang)) {
        lang.helloJava();
    } else {
        lang.helloJavaScript();
    }
}

交叉类型和联合类型

交叉类型:将多个类型合并成一个类型,新类型具有所有类型特性

interface DogInterface {
    run(): void
}
interface CatInterface {
    jump(): void
}

let pet: DogInterface & CatInterface = {
    run() {},
    jump() {}
}

联合类型:取值可以是多个类型的一种

// 类型联合
let a: number | string = 1
// 取值联合 限定取值范围
let b: 'a' | 'b' | 'c' 
let c: 1 | 2 | 3  

其他应用类型

索引类型

interface Obj {
    a: number;
    b: string;
}
let key: keyof Obj   // let key: "a" | "b"

let value: Obj['a'] // let value: number

// 示例
let obj = {
    a: 1,
    b: 2,
    c: 3
}
function getValues<T, K extends keyof T>(obj: T, keys: K[]): T[K][] {
    return keys.map(key => obj[key])
}
console.log(getValues(obj, ['a', 'b']))
console.log(getValues(obj, ['d', 'e'])) // 报错

映射类型

interface Obj {
    a: string;
    b: number;
}
type ReadonlyObj = Readonly<Obj> 
// type ReadonlyObj = {
    // readonly a: number;
    // readonly b: string;
// }

type PartialObj = Partial<Obj>
// type PartialObj = {
//     a?: number | undefined;
//     b?: string | undefined;
// }

type PickObj = Pick<Obj, "a" | "b">
// type PickObj = {
//     a: number;
//     b: string;
// }

type RecordObj = Record<"x" | "y", Obj>
// type RecordObj = {
//     x: Obj;
//     y: Obj;
// }

条件类型

// T extends U ? X : Y
type TypeName<T> =
    T extends string ? "string" :
    T extends number ? "number" :
    T extends boolean ? "boolean" :
    T extends undefined ? "undefined" :
    T extends Function ? "function" :
    "object";
type T1 = TypeName<string>  // type T1 = "string"
type T2 = TypeName<string[]> // type T2 = "object"

// (A | B) extends U ? X : Y  等价于 (A extends U ? X : Y) | (B extends U ? X : Y)
type T3 = TypeName<string | string[]>  // type T3 = "string" | "object"

type Diff<T, U> = T extends U ? never : T 
type T4 = Diff<"a" | "b" | "c", "a" | "e"> // type T4 = "b" | "c"

type NotNull<T> = Diff<T, null | undefined>
type T5 = NotNull<string | number | undefined | null>  // type T5 = string | number

// Extract<T, U>  抽取T,U中的交集
type T6 = Extract<"a" | "b" | "c", "a" | "e">  //type T6 = "a"

// ReturnType<T>  获取函数返回值的类型
type T8 = ReturnType<() => string>