「面试」-TS泛型

558 阅读2分钟

参考一些博客,整理的TS相关,供自己复习使用~

TS泛型

指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性。 使用<T>代表类型的变量,T只是约定用法,可以任意指定。

泛型无法知道具体的类型,所以无法操作它的属性和方法

function Test<T> (a:T):void {
  console.log(a.length);  // error
}
Test<string>('1')

在明确知道泛型中有哪些属性方法时,可以通过extends进行泛型约束,写在声明函数名的后面

interface hasLengthProp {
    length : number;
}
function Test<T extends hasLengthProp>(a:T):void {
    console.log(a.length);
}

泛型可以约束泛型 ---> 相当于泛型的继承

function test<T extends U,U>(a:T,b:U):void {
    console.log(a);
}

test({a:1,b:2,c:3},{a:1,b:2})

在函数中使用泛型

案例一

function join<T> (a: T, b: T) {
  return `${a} ${b}`;
}

join<number>(1, 2);
join<string>('1', '2');
// join<number>(1, '2');
// join<string>(1, '2');

案例二

function getArr<T>(arr: T[]) {
  return arr;
}

getArr<number>([1, 2, 3]) //指定了number,那我的数组必须每一项也是number,如果不是就报错
getArr<string>(['g', 'q', 'f']) //同理这里指定了string

案例三

function getVal<T>(obj: T, k: keyof T){
  return obj[k];
}

interface Person {
  name: string;
  age: number;
}

getVal<Person>({
  name: 'gqf',
  age: 29
}, 'age') // 这里的key值只能传name或者age,否则就会报错,这个就是泛型的力量

案例四

function manyTest<K, V>(a: K, b: V) {
  return `${a} ${b}`
}

manyTest<number, string>(1, '2') 
//泛型指定了第一个参数是数字,第二个参数是字符串,所以对应的参数也要这么传

在类中使用泛型

案例一

class DesignHero {
  constructor(private skills: string[]){}

  getSkill (index: number) {
    console.log(this.skills[index])
    return this.skills[index];
  }
}

const hero = new DesignHero(['q', 'w', 'e', 'r']) // string[] 所以传入字符串数组
hero.getSkill(3)

案例二

class DesignHero {
  constructor(private skills: number[]){} // 这行从string[]改成number[]

  getSkill (index: number) {
    console.log(this.skills[index])
    return this.skills[index];
  }
}

const hero = new DesignHero([1, 2, 3, 4]) // number[] 所以传入数字数组
hero.getSkill(3)

案例三

class DesignHero<T> {
  constructor(private skills: T[]){}

  getSkill (index: number) {
    console.log(this.skills[index])
    return this.skills[index];
  }
}

const heroNumberSkill = new DesignHero<number>([1, 2, 3, 4]) 
heroNumberSkill.getSkill(1)
const heroStringSkill = new DesignHero<string>(['q', 'w', 'e', 'r']) 
heroStringSkill.getSkill(2)

案例四

class DesignHero<T extends string | number> { 
// 如果把继承的string | number 比如改成boolean, 那下面肯定就报错了
  constructor(private skills: T[]){}

  getSkill (index: number) {
    console.log(this.skills[index])
    return this.skills[index];
  }
}

const heroNumberSkill = new DesignHero<number>([1, 2, 3, 4])
heroNumberSkill.getSkill(1)
const heroStringSkill = new DesignHero<string>(['q', 'w', 'e', 'r'])
heroStringSkill.getSkill(2)

案例五

interface Skill {
  name: string;
  canDamage: boolean; // 是否是直接造成伤害的技能
}

class DesignHero<T extends Skill> { // 规定了数组每一项的Skill技能,要遵循接口的格式,有name和canDamage字段
  constructor(private skills: T[]){}

  getSkillName (index: number) {
    console.log(this.skills[index].name)
    return this.skills[index].name;
  }
}

const finalHero = new DesignHero([
  {
    name: '一技能',
    canDamage: true,
  },
  {
    name: '二技能',
    canDamage: false,
  },
  {
    name: '三技能',
    canDamage: false,
  },
  {
    name: '四技能',
    canDamage: true,
  }
])

finalHero.getSkillName(0)

在接口中使用

interface IResponseData<T>{
    code: number;
    message?: string;
    data: T;
}

// 用户接口
interface IResponseUserData{
    id: number;
    username: string;
    email: string;
}

// 文章接口
interface IResponseArticleData{
    id: number;
    title: string;
    author: IResponseUserData; 
} 

async function getData<U>(url: string){
    let response = await fetch(url);
    let data: Promise<IResponseData<U>> = await response.json(); // 注意这里返回的是个Promise,然后我们根据不同的接口,指定不同的data数据格式
    return data;
} 

(async function(){
    let userData = await getData<IResponseUserData>('/user');
    userData.data.username;

    let articleData = await getData<IResponseArticleData>('/article');
    articleData.data.author.email;
})()