是否可以有以抽象方式描述型別的超類/介面和子類/實作?
假設您有型別和以下功能:
class Animal {
name: string;
}
class Bird extends Animal {
canFly: boolean
}
class Dog extends Animal {
tailLength: number;
}
enum animals {
bird = 'bird',
dog = 'dog'
}
class AnimalDTO {
type: animals;
information: Animal;
}
function registerAnimal(animalDto: AnimalDTO) {
if (animalDto.type == animals.dog) {
petDog(animalDto.information);
}
}
function petDog(dog: Dog) {
console.log(`Petting ${dog.name} with tail length ${dog.tailLength}`)
}
現在在 registerAnimal 函式中它抱怨
Argument of type 'Animal' is not assignable to parameter of type 'Dog'.
Property 'tailLength' is missing in type 'Animal' but required in type 'Dog'.
我怎樣才能擁有一個接受這個全域 DTO 的函式,并且我可以根據傳遞的型別應用邏輯,將傳遞的資訊投影到特定的子類?
uj5u.com熱心網友回復:
問題是您正在嘗試縮小animalDto.informationbased on的型別animalDto.type,這僅適用animalDto于特定動物的 DTO 聯合(更多內容見下文)。
這樣做的通常方法是沒有Animal基類,而是有一個特定動物型別的聯合型別(這被稱為有區別的聯合 ——一個你可以通過某些特征來區分成員的聯合[name在這種情況下]) :
interface Bird {
name: "bird";
canFly: boolean;
}
interface Dog {
name: "dog";
tailLength: number;
}
type Animal = Bird | Dog;
(我在那里使用了介面,但你也可以使用Bird和Dog類來實作。)
這樣,我們可以根據動物的型別name(字串文字型別,"bird"或者"dog",不僅僅是字串)來區分動物:
interface AnimalDTO {
information: Animal;
}
function registerAnimal(animalDto: AnimalDTO) {
const animal = animalDto.information;
if (animal.name == "dog") {
petDog(animal);
}
}
function petDog(dog: Dog) {
console.log(`Petting ${dog.name} with tail length ${dog.tailLength}`);
}
游樂場鏈接
如果AnimalDTO必須有一個單獨的type,你可以用它做同樣的事情,我Bird和Dog上面做的一樣:
interface BirdDTO {
type: "bird";
information: Extract<Animal, {name: "bird"}>;
}
interface DogDTO {
type: "dog";
information: Extract<Animal, {name: "dog"}>;
}
type AnimalDTO = BirdDTO | DogDTO;
function registerAnimal(animalDto: AnimalDTO) {
if (animalDto.type == "dog") {
petDog(animalDto.information);
}
}
該位標識與我們用于 DTO的匹配Extract的特定名稱。nametype
游樂場鏈接
如果有很多動物,在兩個地方重復"bird"and"dog"很容易出錯,所以我們可以使用泛型型別來創建它們:
type MakeAnimalDTO<AnimalType extends string> = {
type: AnimalType;
information: Extract<Animal, {name: AnimalType}>;
}
type BirdDTO = MakeAnimalDTO<"bird">;
type DogDTO = MakeAnimalDTO<"dog">;
type AnimalDTO = BirdDTO | DogDTO;
游樂場鏈接
有很多方法可以做到這一點,但最基本的是要有一種基于型別的方法來區分型別。在上面那是"bird"和"dog"字串文字型別。
uj5u.com熱心網友回復:
type告訴 TypeScript和information屬性之間存在關系有點尷尬。但是您可以在單獨的型別中定義所有可能的關系:
class AnimalDTO {
type!: animals
information!: Animal
}
type AnimalDTOtype = {
type: animals.bird,
information: Bird
} | {
type: animals.dog
information: Dog
}
function registerAnimal<T extends keyof typeof animals>(animalDto: AnimalDTOtype) {
if (animalDto.type == animals.dog) {
petDog(animalDto.information);
}
}
uj5u.com熱心網友回復:
你可以做:
class Animal {
name!: string;
}
class Bird extends Animal {
canFly!: boolean;
}
class Dog extends Animal {
tailLength!: number;
}
enum EAnimalType {
bird = 'bird',
dog = 'dog',
}
class AnimalDTO {
type!: EAnimalType;
information!: Animal;
tailLength?: number;
}
function petDog(dog: Dog) {
console.log(`Petting ${dog.name} with tail length ${dog.tailLength}`);
}
function registerAnimal(animalDto: AnimalDTO) {
if (animalDto.type == EAnimalType.dog) {
const dog = new Dog();
dog.name = animalDto.information.name;
dog.tailLength = animalDto.tailLength || 0;
petDog(dog);
}
}
registerAnimal({
type: EAnimalType.dog,
information: {
name: 'Yaki',
},
tailLength: 3,
});
檢查作業代碼:www.typescriptlang.org/play
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/461301.html
標籤:javascript 打字稿
