考虑一个经典问题,在这个问题中区分联合很有用:
type TShape = {
type: string;
}
type TDrawing = {
shapes: Array<TShape>;
}
const TypeRect = "rect";
type TRectangle = TShape & {
type: typeof TypeRect; //correct?
width: number;
height: number;
}
const TypeCircle = "cricle";
type TCircle = TShape & {
type: typeof TypeCircle; //correct?
radius: number;
}
和更多
然后是上述定义的一个应用:
const drawing: TDrawing = {
shapes: [
{ type: TypeRect, width: 2, height: 3 }, //error
{ type: TypeCircle, radius: 5 }, //error
{ type: TypeRect, width: 10, height: 5 }, //error
]
}
function getCircles(drawing: TDrawing): Array<TCircle> {
const a: Array<TCircle> = [];
for (const shape of drawing.shapes) {
if (shape.type === TypeCircle) {
a.push(shape); //error
}
}
return a;
}
现在,考虑TShape
派生的类型对于定义TShape
的库来说不一定是已知的(考虑包含其他形状的第二个库)。
第一个疑问:我需要TShape
级别的type
场来处理任何一种形状的最基本结构。然而,我还需要type
专门化来区分实际的形状。在派生类型中重新定义字段是否正确?
第二个问题:我找不到一种方法让编译器推断出正确的类型,无论是在数组定义中,还是在循环中。有没有一种方法可以同时解决这两个问题?
我想你在寻找这样的东西:
type TShape = {
type: string;
id: string;
}
type TRectangle = TShape & {
type: "rect";
width: number;
height: number;
}
type TCircle = TShape & {
type: "circle";
radius: number;
}
type Circle = 'circle'
type Rect = 'rect'
type Shapes = TRectangle | TCircle
type TDrawing = {
shapes: Array<Shapes>;
}
type TPolygon = TShape & {
edgeCount: number;
}
const drawing: TDrawing = {
shapes: [
{ type: 'rect', width: 2, height: 3, id: '1' }, //ok
{ type: 'circle', radius: 5, id: '2' }, //ok
{ type: 'rect', width: 10, height: 5, id: 2 }, // expected error, id should be string
{ type: 'circle' }, // expected error, no id, no raduis
{ type: 'circle', edgeCount: 8 }, // expected error, you don't allow extra properties
]
}
const requiredProps = ['id']
const hasProperties = <T, P extends string>(obj: T, props: P[]) => props.every(prop => Object.prototype.hasOwnProperty.call(obj, prop))
const isCircle = (shape: Shapes): shape is TCircle => shape.type === 'circle' && hasProperties(shape, ['radius', ...requiredProps])
const isRect = (shape: Shapes): shape is TRectangle => shape.type === 'rect' && hasProperties(shape, ['width, height', ...requiredProps])
const getCircles = (drawing: TDrawing): Array<TCircle> => drawing.shapes.filter(isCircle)
const getRect = (drawing: TDrawing): Array<TRectangle> => drawing.shapes.filter(isRect)
请告诉我是否符合你的要求。
我不确定你是否知道前面所有可能的联合类型
操场上联系