大前端开发你可能不知道的JavaScript JavaScript

2021一份热乎的万字TS教程

2021-02-10  本文已影响0人  林一一呢

推荐阅读地址

掘金

github 求 start😄😄

介绍Typescript

Typescript 是什么?

image

图片来源网络,侵权删除

图灵

下载安装及使用

动态语言和静态语言的差别

// TS
function tsFunc (data: {x: number, y: number}) {
    console.log('demo ts')
    return Math.sqrt(data.x ** 2 + data.y ** 2)
}

// tsFunc()  // 没有传入参数,这里的代码 vscode 会提示错误,这就是静态语言在编写代码的时候就可以知道有错误。
tsFunc({x: 3, y: 4})  //需要将参数代码一起写入。

// 再比如传入参数的个数,ts 能直接检测,但是生成的 js 文件不能检测
function get(param) {
    return param
}

get('hello')

get('hello', 1) // error: 应有 1 个参数,但获得 2 个
// 静态类型和动态类型的差别,静态类型在编写代码时就可以发现错误像C++,Java等,动态类型的语言则需要代码运行时才可以知道错误,像JavaScript,python。
// js-code

function jsFunc (data) {
    return Math.sqrt(x ** 2 + y ** 2)
}

jsFunc()  // 没有传入参数,但这里的代码 vscode不会提示错误,但实际运行会发生报错。

二. 静态类型

静态类型

就像前面看到的那样静态类型可以是基础类型 number string null undefined symbol boolean void enum 还可以是对象类型 Object,Array, class, function,还可以是自定义类型 interface 或任何类型 any 等详情 typescript官网

. 基础类型 number string null undefined symbol boolean any void never。。。

const num: number = 123
const Name: string = 'LinYY'
const boolean: Boolean = true
let n: null = null
let u: undefined = undefined

undefined 类型。可以作用到可选类型,因为可选的类型默认会有一个undefined 类型

interface E {
    b: number
    c?: number
}

let e: E = {b: 12, c: 12}

e = {b: 23, c: undefined}

注意 any 类型,any 类型定义后可以修改为其他的类型

// any 类型可以修改成其他任何类型,TS 不对 any 类型作类型检测
let not: any
not = 2
not = '2'
not = true

// 处理不确定的数组类型 any 比较合适。
let listArr: any[] = ['1', 2, true]

void 类型 通常作用在函数中代表没有返回值,虽然也可以作为其他变量的类型,但只能赋值成 undefined。换一个方向想函数总是有返回值的,如果不是一个确定的值那么就是 undefined 值,所以 void 其实是属于 undefined 的,所以一个变量类型是 void 时,值只能 undefined 值。但是 不能将类型“void”分配给类型“undefined”详情看例子

// void 空类型,一般用于函数,
function noReturn(): void {
    console.log('no value return')
}

function fn(): void {
    // Error
    return 3;
  }

function fn5(): void {
}
let un: undefined = fn5(); // Error 不能将类型“void”分配给类型“undefined”

let voidValue: void = undefined
let voidValue2: void = null   // 不能将类型“null”分配给类型“void”

// never 类型,不会执行结束的函数类型
function errorFunc(): never {
    throw new Error()
    console.log('never')    // 抛出错误后 这段代码不打印。
}

function abs(): never {
    while (true) {
    }
    console.log('never')    // 上面的代码永远是true 这段代码不打印。
}
let person: {
    name: string,
    age: number
} = {
    name: 'LinYY',
    age: 12
}

// 或 (不推荐写法)
let personB:{name: string} & {age: number} = {
    name: 'LinYY',
    age: 12
}
const list: number[] = [12, 23, 34]

//等同于,下面的数组泛型,泛型是什么之后会讲,先留一个印象。
const listA: Array<number> = [1, 2, 3]

// const listB: number[] = ['12', 23, 34]
class Person {}
const LinYY: Person  = new Person()
const getNumber: () => number = () => {
    // return 'LinYY'   报错
    return 123
}

// 要求返回值是string 字符类型
const getString: () => string = () => {
    return 'LinYY'
    // return 123
}
interface Point {
    x: number,
    y: number
}

const point: Point = {
    x: 2,
    y: 4
}
// 变量的类型可以有多个,比如可以是number或string类型。
let temp: number | string = 23
temp = '23'
type User = { name: string, age: number }
let male: User = {name: 'LinYY', age: 18}
let famale: User = {name: 'nana', age: 18}

小 tip

注释小技巧tip: 使用 /** */ 可以给类型添加更友好的提示

// 注释
/**
 * this is good Per
 */
interface Per {
    name: string
}

const p: Per ={
    name: 'LinYY'
}
注释
注释

在 typescript 里面 name 是一个预留关键字,不能直接当变量来用

三.类型注解和类型推断

type annotation 类型注解。

// type annotation 类型注解。
let count: number
count = 23

type inference 类型推断。

let countB = 23
let countC  // any 类型
countC = 233

TS 并不能所有的类型都能推断出来,那么什么时候使用类型注解呢?具体情况需要具体分析

let num1 = 1
let num2 = 2
let sum = num1 + num2   // TS 推断出 sum 是 number 类型

let obj = {
    name: 'LinYY',
    age: 18
}

// obj.name = 23 // TS 推断出来的类型 同样不能再修改

// 需要类型注解
function getSum(a, b) {
    return a + b
}

const total = getSum(2, 3)

四.TS 函数

TS 定义函数的方法和 JS 基本一样,不同的是 TS 可以要求有无返回值。

// 要求返回值是 number 数字类型,下面两种写法等价。
const fooFunc = (a: number, b: number): number => {
    return a + b
}

//: (a: number, b: number) => number' 这里的具体意义是函数参数 a, b 类型是 number 型,返回值是 number 型,后面的 = 是跟函数的具体实现
const foo1: (a: number, b: number) => number = (a, b) => {
    return a + b
}

// 返回值是 void 空类型
function sum2(a: number, b: number): void {
    console.log(a + b)
}

// 联合类型
function sum2(a: number, b: string): number | string {
    return a || b
}

TS 函数的类型解构正确用法

// 避免意外的 bug,当传入的 person 中没有 name 属性时下面的代码会报错,TS 能规避这个问题给出报错提示
const getNameA = (person: {}) => {
    console.log(person.name)  // ==> undefined
}

const getName1 = (person: { name: string }) => {
    console.log(person.name)
}
// 不加入对象类型的注解{a: number, b: number},返回值则不能保证是预期的类型.
function add({ a, b }: { a: number, b: number }): number {
    const c = a + b
    return c
}
const sum1 = add({ a: 1, b: 2 })

// 参数为对象的正确注解方式
function getNumberA({ a }: { a: number }) {
    return a
}
const totalA = getNumberA({ a: 1 })

// 如这里不能保证放回值是number类型,因为a, b类型为any类型。
function add2({ a, b }): number {
    const c = a + b
    return c
}

TS 中的 this,TS 是 JS 的超集,this 的指向法则都一样

this 指向最后调用 this 的对象

this 指向 示例一

let name = 'foo'
let f = {
    name: "Lin",
    a: function () {
        console.log(this.name) // ==> Lin
    }
}

f.a() // f.a() == window.f.a()  
// 为什么是 Lin?因为 对象 f 调用了函数 a(),再调用了 this。上面 window 调用了 f 对象,但是 f 才是最后调用 this 的对象

再看示例二

var age = 18

function foo() {
    let age = 20
    console.log(this.age) // ==>18
}

foo() // == window.foo()
//为什么是18,不是20呢?上面 foo() 函数是 window 对象调用了,所以 this 指向 window,那么 使用的 age 也就是window下的 age

再看示例三

let name = 'LinYY'
let a = {
    name: 'lin',
    c: function () {
        return function () {
            console.log(this.name)
        }
    }
}
let b = a.c()
b()     // window.b()
// 为什么这里 打印的是 LinYY 呢?和示例二类似,b() 最后是被 window 对象调用了,所以还是 “this 指向最后调用 this 的对象”
this打印结果

箭头函数解决 this指向

// 问:如果就是想要使用对象 a 上下文呢?那么就可以使用 箭头函数。保存最近的上下文的this,也就是这里的对象 a
let name = 'LinYY'
let a = {
    name: 'lin',
    c: function () {
        return () =>  {
            console.log(this.name)
        }
    }
}
let b = a.c()
b()
箭头函数打印结果

五.数组和元组

数组的类型注解

数组的类型声明分为两种,一: 类型[], 二: Array<类型>,两种写法等价具体看下面示例

// 单类型注解数组 [ ],number[] 表示是数字类型的数组,其他同理
const numberArr: number[] = [1, 2, 3]   // 等同于 const numberArr: Array<number> = [1, 2, 3]
const stringArr: string[] = ['1', '2', '3']  // // 等同于 const stringArr: Array<string> = ['1', '2', '3']
const undefinedArr: undefined[] = [undefined]

// 多类型注解数组 [ ]
const arr: (number | string)[] = [1, 2, '2']

// 对象数组
const objectArr: { name: string, age: 18 }[] = [{
    name: 'LinYY',
    age: 18
}]
// 采用类型别名 type alias
type User = { name: string, age: number }

const objectArr1: User[] = [{
    name: 'LinYY',
    age: 18
}]
// 数据结构一致 TS 不会报错
class Teacher {
    name: string = ''
    age: number = 0
}

// 这里是一个 Teacher 类,那么每一个元素都应该是 Teacher 的实例,但是由于下面的对象数据结构和 Teacher类一致,所以 TS 没有报错。
const objectArr2: Teacher[] = [
    new Teacher(),
    {
        name: 'LinYY',
        age: 18
    }]

// 反例:因为 Teacher 类中没有 other 属性,TS 会提示 “other”不在类型“Teacher”中
 const objectArr3: Teacher[] = [
     new Teacher(),
     {
     name: 'LinYY',
     age: 18,
     other: 0
 }]

元组 tuple 是一种特殊的数组 (TS 新增)

// tuple 元组的定义
let arrT: [number, string, number]
arrT = [18, 'LinYY', 3]

// 元素操作方法
arrT[0].toExponential(1)
arrT[1].split('i')
arrT[2] = 4

// 类型不对应就会报错
arrT = [18, 'LinYY', '1']

// 越界元素,直接报错
arrT[3] = '12' // 不能将类型“"12"”分配给类型“undefined”

type tupleArr =  [number, string, number]
const arrTB: tupleArr = [18, 'LinYY', 3]

const attTC: tupleArr[] =[
    [18, 'LinYY', 3],
    [1, 'LinYY', 23],
    [2, 'LinYY', 13]
]

// 普通数组不能约束每一项元素的类型,下面元素的类型就不可以约束
let list: (number | string)[] = ['LinYY', 18] 
let listB: (number | string)[] = [18, 'LinYY']
/**巧用元组 */
function getParams(...arry:[string, number, boolean]){
    const str: string = arry[0];
    const num: number = arry[1];
    const b: boolean = arry[2];
}

六.interface 接口

// 一个简单实例示例说明 interface 是一个类对象
interface PersonA {
    firstName: string,
    lastName: string
}

function greeter(person: PersonA) {
    return person.firstName + person.lastName
}

let userA = {
    firstName: 'lin',
    lastName: 'YY'
}

greeter(userA)
// 可能用到的属性,在属性后面加上'?',
interface Person {
    name: string,
    age: number,
    age1?: number,   // age1 是接口可能用到的属性。
    readonly ID: number
}

const person: Person = {
    name: 'LinYY',
    age: 18,
    // age1: 0,
    ID: 101
}
// 只读操作 readonly 不传入 age1 也可以通过校验
const getName = (person: Person) => {   // Person 是上面的 interface 接口
    console.log(person.age)
    // console.log(person.age1)  ==> 0
    console.log(person.ID)
    // person.ID = 200     // 报错 ID只能读取,不能修改==> error TS2540: Cannot assign to 'ID' because it is a read-only property
}

getName(person)
// interface 中定义方法
interface Search{
    (a: number, b: number): boolean
}

let search: Search
search = function(a: number, b: number): boolean {
    return a >= b
}
search(2, 3)

// 或
interface Action {
    name: string,
    age: number,
    say(): string
}

const applySay = (action: Action) => {
    console.log(action.say())
}

const action = {
    name: 'LinYY',
    age: 18,
    say() {
        return 'hello TS'
    }
}

applySay(action)

// 下面定义了一个 say 类型的接口 接受一个 string 的参数,返回 string 类型的字符串
interface Say {
    (word: string): string
}

const foo: Say = (word: string) => {
    return word
}

foo('hello TS')   //  ==> 'hello TS'
/**interface 间还能相互嵌套 */
interface A {
    name: string,
    age: number
}

interface B {
/** person is interface a */
    person: A
}
// implements 和 extend 不同 extend 是继承父类,implement 是实现接口 interface 而且可以使用多个接口,用逗号隔开。
// class A extends B implements C,D,E

interface Person {
    name: string,
    age: number,
    age1?: number,   // age1 是接口可能用到的属性。
    readonly ID: number
}

class test implements Person {
    name = 'LinYY'
    age = 18
    ID = 301
}
// 被其他接口继承 extends
interface Music {
    click: boolean
}

interface Sentence {
    color: string
}

interface Classic extends Music, Sentence {
    time: number
}

let classic = {} as Classic
classic.click = false
classic.color = 'white'
classic.time = 220

// 或
interface PersonB {
    name: string,
    age: number,
    age1?: number,   // age1 是接口可能用到的属性。
    readonly ID: number
}

interface Teach extends PersonB {
    action(): string
}

const teach: Teach = {
    name: 'LinYY',
    age: 28,
    ID: 501,
    action() {
        return '222'
    }
}

// 或使用 '<类型>' 表示类型
const teachA = <Teach>{
    name: 'LinYY',
    age: 28,
    ID: 501,
    action() {
        return '222'
    }
}
// interface 继承 class 示例
class Animal {
     fly: any
}

interface Dog extends Animal {
    run(): void
}

注意点

// 示例二
function printName(obj: { name: string }) {
    console.log(obj.name)
}

let myObj = { name: 'LinYY', age: 18 }
printName(myObj)

// 或
const getAge = (person1: Person) => {
    console.log(person1.age)
}

const per = {
    name: 'LinYY',
    age: 18,
    ID: 201,
    sex: 'male'     // 不在 Person 接口内,也可以通过校验
}
getAge(per)
// 以字面量的形式传入,TS 会强校验导致校验不通过。
 getAge({
     name: 'LinYY',
     age: 18,
     ID: 201,
     sex: 'male'     // ==>  'sex' does not exist in type 'Person'
 })

interface 实际上在编译成 JS 后并没有相应的代码,其实 interface 就是 TS 来约束代码代码规范的。

怎么有效解决以后开发的过程中可能会加入的属性呢?

// 示例三 [propName: string]: any
interface User {
    name: string,
    age: number,
    [propName: string]: any // 后期可能用到的 string 类型的属性
}

const getSex = (user: User) => {
    console.log(user.age)
}

const user = {
    name: 'LinYY',
    age: 18,
    ID: 201,
    sex: 'male'     // 不在 User 接口内,但也能通过
}

getSex(user)

// 以字面量的形式传入也可以。
getSex({
    name: 'LinYY',
    age: 18,
    ID: 201,
    sex: 'male'
})

interface 和 type alias 关系

两者用法上面没有太大的区别,同样都可以扩展,只是语法不同,type 使用交叉类型 &。而且两者相互之间可以继承。但是 interface的应用场景更加的广,能够使用interface就不用type

// interface 和 type alias
/** ifc is interface */
interface Ifc {
    name: string,
    age: number
}

/** T is type alias */
type T = {
    name: string,
    age: number
}

/* interface 和 type alias 扩展示例 */
interface IfcName {
    name: string
}
interface IfcAge extends IfcName {
    age: number
}

type TName = {
    name: string
}
/** TAge 继承了 TName 的 name 属性*/
type TAge = TName & {
    age: number
}
const tAge : TAge = {
    name: 'LinYY',
    age: 18
}

/* interface 和 type alias 相互继承示例 */

/** interface  extends type alias */
interface IfcAge extends TName {
    age: number
}

/** type & interface alias */
type TypeName = IfcAge & {
    name: string
}

更多细节 官网 /

image

七.class 类

TS 中类的继承和定义和 JS 基本一致

// TS 中类的定义
class PersonA {
    name: string
    constructor(msg) {
        this.name = msg
    }
    getName() {
        return this.name
    }
}

const personA = new PersonA('LinYY')
console.log(personA.getName())   // ==> LinYY

// TS 类的继承
class Student extends Person {
    say() {
        return this.name
    }
    getName() {
        return 'LinYY'  // 重写父类 getName方法
    }
}
const student = new Student()
console.log(student.say())   // ==> LinYY

TS 类中的访问类型,构造器和 JS 也基本一致

访问类型 private,public,protected

// constructor 示例
class PersonB {
    public name: string
    constructor( name: string) {
        this.name = name
    }
}

// 简化写法,推荐。
// class PersonB {
//     constructor(public name: string) {
//     }
// }

const personB = new PersonB('LinYY')

class TeacherA extends PersonB {
    constructor(public age: number ) {
        super('LinYY')  // 初始化父类的 name
    }
}

const teacher = new TeacherA(18)
// static 
class GetAge {
    static  age = 18
    static printAge() {
        console.log(GetAge.age)
    }
    static setAge(msg: number) {
        this.age = msg
        this.printAge()
    }
}
// 将 class 当作接口使用
class A {
    x: number
    y: number
    constructor(x: number, y: number) {
        this.x = x
        this.y = y
    }
}

interface B extends A {
    z: number
}

let printA: A = {x: 2, y: 3}

TS 类中的静态属性,取值函数(getter)和存值函数(setter),对某个属性设置存值函数和取值函数,拦截该属性的存取行为。

Getter 的使用

Setter 的使用

class Person {
    constructor(private _name: string){}    // 私有属性一般加下划线 '_'
    get name() {
        return this._name
    }
    set name(rename: string) {
        this._name = rename
    }
}

// get
const person = new Person('LinYY')
console.log(person.name)    // ==> LinYY 调用 get 下面的 name 属性

// set
person.name = 'LinYYB'  // 调用 set 下面的 name 属性同时赋值
console.log(person.name)    // ==> LinYYB

修饰器 readonly 也可以设置类的只读属性

// 装饰器`readonly`用来装饰“类”的`name`属性。
class PersonB {
    readonly name: string
    constructor(private _name: string){
        this.name = _name
    }    // 私有属性一般加下划线 '_'
}

抽象类 abstract 目的是将有很多共性的方法或属性抽离出来

import axios from 'axios'

// 抽象类 abstract
abstract class Classic {
    name: string
   async submit() {
        return 'LinYY'
    }
    abstract getApiData()   // 一个抽象方法
}

// const classic = new Classic()   //  error: 无法创建抽象类的实例,只能被继承

class Music extends Classic {
    async getApiData() {
      return await axios.get('api1URL').then( res => {
          console.log(res)
      }).catch( e => {
        console.log(e)
    })
    }
}

class Book extends Classic {
    async getApiData() {
      return await axios.get('api2URL').then( res => {
          console.log(res)
      }).catch( e => {
          console.log(e)
      })
    }
}

class Sentence extends Classic {
   async getApiData() {
      return await axios.get('api3URL').then( res => {
          console.log(res)
      }).catch( e => {
        console.log(e)
    })
    }
}

八.TS 交叉类型和联合类型和类型保护

交叉类型

// 交叉类型
interface Colors {
    red: string
}

interface Rectangle {
    height: number
    width: number
    area: () => number
}

// param 参数可以访问类型 Colors 和 Rectangle 所有属性
function getArea(param: Colors & Rectangle) {
    param.height = 2
    param.width = 3
    param.red = 'red'
    param.area = (): number => {
        return param.height * param.width
    }
}

联合类型

//  联合类型
let bar: string | number = 12
bar = '12'


interface Bird {
    fly: Boolean;
    sing: () => {}
}

interface Dog {
    fly: Boolean;
    dark: () => {}
}

// animal 参数可以是 Bird 或 Dog,语法提示可以直接提示出共有属性 fly,但是不能直接提示出 sing 和 dark。
function trainAnimal(animal: Bird | Dog) {
    animal.fly
    // animal.dark() 这里直接报错,因为不能确保 animal 包含 dark 方法。
}

类型保护

//类型保护——类型断言 as
function trainAnimal1(animal: Bird | Dog) {
    if (animal.fly) {
        (animal as Bird).sing() //直接告诉 TS 这里 animal 是 Bird 类型
        // 或下面的一种写法
        // (<Bird>animal).sing()
    } else {
        (animal as Dog).dark()
    }
}
// in 判断
function trainAnimal2(animal: Bird | Dog) {
    if ("sing" in animal) {   // 判断 animal中是否含有私有属性 sing
        animal.sing()
    } else {
        animal.dark()
    }
}
// typeof 类型保护
function trainAnimal3(paramA: number | string, paramB: number | String) {
    if (typeof paramA === "string" && typeof paramB === "string") {
        return paramA + paramB
    }
    return
}
// null 类型保护
function fn(params: string | null) {
    // params.length   // 对象可能为 "null"
    return params!.length   // 将参数的可能类型 null 类型排除
}

fn('12121')
fn(null)

九.Enum 枚举类型

// 枚举是一种数据类型
enum Color {
    Red,
    Blue,
    Black
}


let color: Color
// 类型也只能是枚举 Color 类型
color = Color.Red
color = Color.Blue
console.log('color', color) // ==> 打印出下标 1

// 枚举一般首字母大写
enum Status  {
    // OFFLINE = 1,
    OFFLINE,
    ONLINE,
    OTHERS
}

// 对比常用的 JS 代码
// const Status = {
//     OFFLINE : 0,
//     ONLINE: 1,
//     OTHERS: 2
// }

function getStatus(status: Number) {
    if(status === Status.ONLINE) {
        return 'online'
    }else if (status === Status.OFFLINE) {
        return 'offline'
    }else if(status === Status.OTHERS) {
        return 'others'
    }
    return 'error'
}

const result = getStatus(Status.OFFLINE)
console.log(result)

// 下面代码直接打印出 enum 的下标值
console.log(Status.OFFLINE)
console.log(Status.ONLINE)
console.log(Status.OTHERS)

// 打印下标对应的属性
console.log(Status[0])
console.log(Status[1])
console.log(Status[2])

原理:为什么枚举可以通过下标的方式索引 key 和 反索引 value呢?

// 经过 tsc 编译后的 js 文件
var Color;
(function (Color) {
    Color[Color["Red"] = 0] = "Red";
    Color[Color["Blue"] = 1] = "Blue";
    Color[Color["Black"] = 2] = "Black";
})(Color || (Color = {}));

十.TS 泛型

泛型概念:泛指的类型不具体针对某一特定的类型。

//泛型简单示例
function foo<T>(a, b, c) {
    return a || b || c
}

// 或
function foo<U>(a, b, c) {
    return a || b || c
}

function id<T>(a: T, b: T, c: T): T {
    return a || b || c
}

// 不针对某一特定类型,可以是 number,string,Boolean,interface等类型,且使用的类型必须一致。
interface Type {
    a: number
    b: number
    c: number
}
let t: Type = {a: 2, b: 3, c: 4}

id(1,2,3)
id('1', '2', '3')
id(false, true, false)
id(t.a, t.b, t.c)

// 接口泛型 推荐写法,直接使用泛型参数 T 代表指定类型
interface Uni<T> {
    a: T
    b: T
    c: T
}
let uni: Uni<number> = {a: 2, b:3, c:4}
id(uni.a, uni.b, uni.c)

函数泛型 Generics,泛指的类型和普通类型用法基本一致

function add(a: number | string, b: number | string) {
    return `${a}${b}`
}

// 如果要求输入的参数 a, b只能是同一个类型的参数该怎么实现?下面几种写法都不能实现
add(1, 2)
add('1', 1)
add(1, '1')

// 下面引用函数泛型实现,<T> 就是函数 add 要泛指的类型,后面的参数 a,b都要是这个类型。
function add<T>(a: T, b: T){
    return `${a}${b}`
}


//让输入的类型是 number 或 string,即<T>代表类型 Number或String
add<number>(1, 1)
add<string>('1', '2')


add<number>(1, '1') //提示报错 "1"的类型不是 number 型
add<string>('1', 2)  //提示报错 2 的类型不是 string 型
// 泛型同样可以继承 interface。
interface LengthPro {
    age: number | null
    moreAge: number
}

function arg<T extends LengthPro>(params: T) {
    return params.age || params.moreAge
}

arg({age: 18, moreAge: 20})
arg({age: null, moreAge: 20})
//将函数的泛型指定为 ABC,参数的泛型是数组类型ABC,返回值也是数组类型ABC。
function func<ABC>(a:ABC[]): ABC[]{
    return a
}

// 或
 function func1<ABC>(a:Array<ABC>): Array<ABC> {
     return a
 }
func<number>([123])

// 错误的输入
func<number>(123)  // 类型“123”的参数不能赋给类型“Number[]”的参数
// 多泛型约束
function moreT<T, Y>(a:T, b: Y) {
    return `${a}${b}`
}

moreT<number, string>(1, '2')

// K extends keyof T,让泛型 K 继承 T 的 keyof 的属性
function getObjVal<T, K extends keyof T>(obj:T, key: K) {
    return obj[key]
}

let o = {a: 1, b: 3, c: 4}

getObjVal(o, 'a')
getObjVal(o, 'd')   // 'd' 不是 对象 o 下面的 keyof 属性。

类的泛型

// 类泛型的普通写法
class GenericType<T> {
    numberVal: T
    constructor(numberVal: T) {
        this.numberVal = numberVal;
      } 
    add(x: T, y: T) {
    }
}

let numberType = new GenericType<number>(2)
numberType.numberVal = 1
numberType.add(2, 8)

let stringType = new GenericType<string>('a')
stringType.numberVal = 'b'
stringType.add('a', 'b')

// 或
class GetItem<T>{
    constructor(private data: T[]) { }
    getName(index: number) {
        return this.data[index]
    }
}

const getItem = new GetItem(['LinYY'])
const res = getItem.getName(0)
console.log(res)
// 继承示例 extends
interface personA {
    name: string,
    age: number
}

class GetItemSecond<T extends personA>{
    constructor(private data: T[]) { }
    getName(index: number) {
        return this.data[index].name
    }
}

const getItemSecond = new GetItemSecond([
    {
        name: 'LinYY',
        age: 18            // 这里不传入 age 时会飘红,必须传入所有属性
    }
])
const res1 = getItem.getName(0)
console.log(res1)
// 约束泛型的范围
function getData<T extends number | string>(param: T) {
    return param
}

getData(1)
getData('1')
// 多泛型约束
function moreT<T, Y>(a:T, b: Y) {
    return `${a}${b}`
}

moreT<number, string>(1, '2')

// K extends keyof T,让泛型 K 继承 T 的 keyof 的属性
function getObjVal<T, K extends keyof T>(obj:T, key: K) {
    return obj[key]
}

let o = {a: 1, b: 3, c: 4}

getObjVal(o, 'a')
getObjVal(o, 'd')   // 'd' 不是 对象 o 下面的 keyof 属性。

上面泛型的使用规则不局限在类或函数内

十一.TS 配置项

tsconfig.json文件文档

tsconfig.json是 TS 编译成 JS 代码的辅助文件

常用的 compilerOptions 编译配置项

十二.TS 高级技巧(持续更新中)

很感谢你能看到这里,希望这份教程能对你有一点点帮助 😊,我是林一一,下次见。

掘金

博客地址 体验效果更好。

源码地址 欢迎start (issue),以后会不断的更新内容

参考:《Typescript官网》《Typescript实战技巧》《你不知道的Typescript高级技巧》《Typescript高级技巧》《你不知道的Typescript泛型》《Typescript深入浅出》。。。

上一篇 下一篇

猜你喜欢

热点阅读