Вам не нужно создавать отображение;Вы можете извлечь это из вашего Shape
типа:
class Square {
readonly kind = "square";
size!: number;
}
class Rectangle {
readonly kind = "rectangle";
width!: number;
height!: number;
}
class Circle {
readonly kind = "circle";
radius!: number;
}
type Shape = Square | Rectangle | Circle;
type Kinds = Shape["kind"]; // automatically
// return type is the consituent of Shape that matches {kind: K}
function createShape<K extends Kinds>(kind: K): Extract<Shape, { kind: K }>;
function createShape(kind: Kinds): Shape {
switch (kind) {
case "circle":
return new Circle();
case "rectangle":
return new Rectangle();
case "square":
return new Square();
}
}
createShape("circle").radius; // okay
Ссылка на код
Тип возврата использует Extract<T, U>
, встроенный условный тип для фильтрации объединения T
, чтобы разрешить присваивание только компонентам другого типа U
.
Обратите внимание, что я использовал перегрузку с одной сигнатурой в createShape()
, поскольку компилятор действительно не может проверить, что оператор switch
всегда возвращает что-то, что соответствует неразрешенному условному типу Extract<Shape, { kind: K}>
.
Надеюсь, что это поможет;удачи!
ОБНОВЛЕНИЕ: Вы не просили об этом, но если бы я писал такую функцию, как createShape()
, я мог бы хранить конструкторы, содержащие объект, и использовать индексированный доступ, чтобы проверить компиляторвведите безопасность для меня внутри createShape()
:
const verifyShapeConstructors = <
T extends { [K in keyof T]: new () => { kind: K } }
>(
x: T
) => x;
const badShapeConstuctors = verifyShapeConstructors({
square: Square,
circle: Rectangle, // error!
rectangle: Circle, // error!
})
const shapeConstructors = verifyShapeConstructors({
square: Square,
rectangle: Rectangle,
circle: Circle
});
type ShapeConstructors = typeof shapeConstructors;
type Instance<T extends Function> = T["prototype"];
type Shape = Instance<ShapeConstructors[keyof ShapeConstructors]>;
type Kinds = keyof ShapeConstructors;
function createShape<K extends Kinds>(kind: K): Instance<ShapeConstructors[K]> {
return new shapeConstructors[kind]();
}
Ссылка на код
В этом я использую вспомогательную функцию verifyShapeConstructors()
, чтобы убедиться, что я надеваюне испортите конструктор-ключи.И я пользуюсь тем, что компилятор знает, что конструкторы классов, такие как Square
, имеют свойство "prototype"
типа экземпляра ... поэтому он может использовать индексный доступ к этому свойству для проверки типа экземпляра конструктора.(Встроенный условный тип InstanceType<C>
действует аналогично, но компилятор не может рассуждать об условных типах так же, как о доступе к индексу).
Все это сводится к тому, что createShape()
теперь является однострочной универсальной функцией, которую компилятор проверяет как корректную.
Как я уже сказал, вы не просили об этом, но это может представлять некоторый интерес.