首页 > 解决方案 > 没有宣布所有案件的歧视性工会?

问题描述

考虑一个经典问题,其中可区分联合很有用:

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(想想包含其他形状的第二个库)。

第一个疑问:我需要该级别的type字段TShape来处理任何类型的形状在其非常基本的结构。但是,我还需要type专业化才能区分实际形状。重新定义派生类型中的字段是否正确?

第二个问题:我找不到让编译器在数组定义和循环中推断正确类型的方法。有没有办法解决这两个问题?

标签: typescriptdiscriminated-union

解决方案


我认为您正在寻找这样的形式:

更新


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)

请让我知道它是否符合您的要求。

我不确定您是否了解所有可能的联合类型

游乐场链接


推荐阅读