Различение Союза Классов - PullRequest
1 голос
/ 10 мая 2019

Использование Typescript 3.4 Я пытаюсь провести различие между объединением классов машинописи, основанных на одном доступном только для чтения поле (дискриминатор), расположенном в каждом подклассе;Само по себе это кажется достаточно простым, но я не могу заставить его решить.

Ниже приведена ссылка на игровую площадку, где метод createFruit должен иметь универсальный параметр для фильтрации следующего свойства по дискриминатору.Любая идея будет принята с благодарностью;однако, кажется, позволяет оценивать никогда.

abstract class Fruit {
    abstract readonly fruitType: string;
}

class Banana extends Fruit {
    fruitType = 'banana';
    length = 2;
    color = 'yellow';
}

class Pear extends Fruit {
    fruitType = 'pear';
    roundness = 'very round';
}

class Apple extends Fruit {
    fruitType = 'apple';
    fallOfMan = true;
    hasWorms = true;
}

type KnownFruits = Banana | Pear | Apple;

type FruitTypes = KnownFruits['fruitType'];

type FruitDiscriminator<T extends FruitTypes> = Extract<KnownFruits, { fruitType: T }>;

let createFruit = <T extends FruitTypes>(fruitType: T, props: FruitDiscriminator<T>) => { }

createFruit('pear', {} ) // Argument of type '{}' is not assignable to parameter of type 'never'.

TS Playground

1 Ответ

0 голосов
/ 10 мая 2019

Проблема в том, что fruitType в производных классах имеет тип string, а не строковый литерал, связанный с строковым литералом. Typescript по умолчанию расширяет литеральные типы до базового типа при инициализации полей. Чтобы обойти это, вы можете использовать утверждение as const или сделать поле w readonly (или указать строковый литерал вручную, но это будет дублировать строку)

Решение readonly имеет смысл, так как тип не должен меняться:

abstract class Fruit {
    abstract readonly fruitType: string;
}

class Banana extends Fruit {
    readonly fruitType = 'banana';
    length = 2;
    color = 'yellow';
}

class Pear extends Fruit {
    readonly fruitType = 'pear';
    roundness = 'very round';
}

class Apple extends Fruit {
    readonly fruitType = 'apple';
    fallOfMan = true;
    hasWorms = true;
}

type KnownFruits = Banana | Pear | Apple;

type FruitTypes = KnownFruits['fruitType'];

type FruitDiscriminator<T extends FruitTypes> = Extract<KnownFruits, { fruitType: T }>;

let createFruit = <T extends FruitTypes>(fruitType: T, props: FruitDiscriminator<T>) => { }

// ok
createFruit('pear', {
    fruitType: 'pear',
    roundness: ""
})
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...