typescript - Bruk betinget type i funksjon
Jeg prøver å bruke betinget type for å koble to parametere i en funksjon, men jeg klarte ikke å finne ut hvordan jeg kobler 2 parametere når jeg definerer en funksjon. Nedenfor er et forenklet eksempel eller den levende versjonen.
type Circle = { type: "circle"; data: { radius: number } };
type Rect = { type: "rect"; data: { width: number; height: number } };
type DefinedShape = Circle | Rect;
type FilterShapeByType<Shape, Type> = Shape extends { type: Type }
? Shape
: never;
type DrawShape = <T extends DefinedShape["type"]>(
type: T,
data: FilterShapeByType<DefinedShape, T>["data"]
) => void;
let drawShape1: DrawShape = () => {};
drawShape1("rect", { height: 12, width: 22 }); // pass
drawShape1("rect", { height: 12, width: 22, radius: 23 }); // failed, radius is not defined
drawShape1("circle", { radius: 12 }); // pass
drawShape1("circle", { radius: 12, len: 23 }); // failed, len is not defined
const drawShape2: DrawShape = (type, data) => {
switch (type) {
case "circle":
console.log(data.radius); // failed, Property 'radius' does not exist on type '(FilterShapeByType<Circle, T> | FilterShapeByType<Rect, T>)["data"]'.
break;
case "rect":
console.log(data.height, data.width);
break;
}
};
Det opprinnelige målet er å koble de to parameterne i DrawShape-funksjonen, noe som betyr at når type er circle, bør data være {radius: number}. Jeg bruker den betingede typen FilterShapeByType for å sortere det ut.
Alt godt før jeg prøver å definere en funksjon drawShape2. Jeg forventer at når jeg bruker switch for type for circle, burde TypeScript være smart nok til å anta at data skal være Circle['data'], men det gjorde den ikke. Lurer på om det finnes en enkel måte å få det til automatisk?
Lekeplass
Svar #1
Det er ett triks. Ikke fortell noen :D
Bare bruk navngitte tupler
function drawShape2(...args: [type: 'circle', data: Circle['data']] | [type: 'rect', data: Rect['data']]) {
switch (args[0]) {
case "circle":
const [type1, data1] = args;
break;
case "rect":
const [type2, data2] = args
break;
}
};
Svar #2
Jeg er nysgjerrig på hvorfor koden din ikke fungerer. Det ser ut som den burde fungere, men det gjør den ikke! Jeg kan ha oversett noe, eller det er en begrensning i TypeScript.
Uansett, her er en løsning som jeg foretrekker (ved å bruke typebeskyttelse):
type Shapes = {
circle: { radius: number },
rect: { width: number; height: number }
}
type ShapeTypes = keyof Shapes;
type DrawShape = <T extends ShapeTypes>(type: T, data: Shapes[T]) => void;
const isShapeOf = <T extends ShapeTypes>(expected: T, type: string, data: any): data is Shapes[T] => {
return expected === type;
}
const drawShape: DrawShape = (type, data) => {
if (isShapeOf("circle", type, data)) {
data.radius; // pass
} else if (isShapeOf("rect", type, data)) {
data.radius; // radius does not exist
data.width; // pass
data.height; // pass
}
};
drawShape("rect", { height: 12, width: 22 }); // pass
drawShape("rect", { height: 12, width: 22, radius: 23 }); // failed, radius is not defined
drawShape("circle", { radius: 12 }); // pass
drawShape("circle", { radius: 12, len: 23 }); // failed, len is not defined
Typescript-lekeplass
