简体   繁体   English

TypeScript 有区别的联合

[英]TypeScript discriminated union

I have the interfaces我有接口

interface Teacher {
    id: number;
    name: string;
    category: string;
    age: number;
    workExperience: number;
    isLikeCoffe: boolean;
}

interface CodingTeacher extends Teacher {
    category: 'coding';
    programmingLanguage: string;
}

interface MusicTeacher extends Teacher {
    category: 'music';
    instrument: string;
}

I have User-Defined Type Guards我有用户定义的类型保护

const isMusicTeacher = (teacher: Teacher): teacher is MusicTeacher => {
    return teacher.category === "music";
}

const isCodingTeacher = (teacher: Teacher): teacher is CodingTeacher => {
    return teacher.category === "coding";
}

When i want to get all the music teachers i can do it like this当我想找所有的音乐老师时,我可以这样做

getMusicTeachers(): Array<MusicTeacher> {
  return this.getTeachers().filter(isMusicTeacher);
}

But I also need to get a lot, where only those who meet certain given criteria, and then difficulties arise.但我也需要得到很多,只有那些符合特定标准的人,然后才会出现困难。 Since I only need to get music teachers who are "professional" And i do it like this因为我只需要找“专业”的音乐老师我就这样做

teachers: Teacher[] = [];
type TeacherCategory = "coding" | "music";

getTeachers<T extends Teacher>(category?: TeacherCategory): Array<T> {

  const activeTeachers = this.teachers.filter(t => t.workExperience > 20 && t.isLikeCoffe);

  if (!category) {
    return activeTeachers as T[];
  }

  return activeTeachers.filter(t => t.category === category) as T[];
}

or i need to get one或者我需要一个

public findTeacherById<T extends Teacher>(id: number, category?: TeacherCategory): T {

  return this.teachers.find(t => (!category || t.category == category) && t.id == id);
}

Am I doing the correct description of the return types?我是否对返回类型进行了正确的描述? In this case, when I want to get only those models knowing their category在这种情况下,当我只想让那些模型知道他们的类别时

const teacher: CodingTeacher = this.myService.findTeacherById(1, 'coding');

const teachers: MusicTeacher = this.myService.getTeachers('music');

Playground 操场

In my opinion, you don't have to declare like that:在我看来,你不必这样声明:

const teacher = new MyClass().findTeacherById<CodingTeacher>(1, 'coding');
// or
const teacher: CodingTeacher = this.myService.findTeacherById(1, 'coding');

Using Intersection type can help you get the type you want without declaring type or passing generics.使用Intersection 类型可以帮助您获得您想要的类型,而无需声明类型或传递 generics。

The following is core idea:以下是核心思想:

type TFn1 = (a: number) => number;
type TFn2 = (a: string) => string;
// be careful of this any here
const Fn: TFn1 & TFn2 = (a: number | string): any => {
  return a;
};

Fn(1); // type is (a: number) => number
Fn('2'); // type is (a: string) => string
Fn(true); // error

Then let's take findTeacherById as an example, it can be:那么我们以findTeacherById为例,可以是:

type T1 = (id: number, category: 'coding') => CodingTeacher;
type T2 = (id: number, category: 'music') => MusicTeacher;

...

public findTeacherById: T1 & T2 = (id: number, category: TeacherCategory): any => {
  return this.getTeachers().find((t) => (!category || t.category === category) && t.id === id);
}
...

const teacher1 = new MyClass().findTeacherById(1, 'coding'); // type returned is CodingTeacher
const teacher2 = new MyClass().findTeacherById(2, 'music'); // type returned is MusicTeacher
const teacher3 = new MyClass().findTeacherById(3, 'test'); // 'test' is not assignable
};

To unify T1 and T2 for convenience, we can declare one uniform type:为了方便统一T1T2 ,我们可以声明一个统一类型:

type TU<T> = (id: number, category: T extends { category: string } ? T['category'] : never) => T;

type TFindTeacherById = TU<CodingTeacher> & TU<MusicTeacher>;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM