ts 根据一个字段的类型,限定另一个字段的类型;或者说让某个字段的类型,由另一个字段决定

本文讨论了在TypeScript中如何通过枚举、接口和类型推断来确保字段类型的正确性,以提高代码的可读性和一致性,通过实例展示如何处理问卷问题类型和形状类型的数据结构。
摘要由CSDN通过智能技术生成

他人案例一(优秀!)

先指出问题所在:

enum ShapeType {
    Square = 1,
    Circle = 2
}
interface Square {
    size: number;
}
interface Circle {
    radius: number;
}
type Shape = {
    type: ShapeType;
    data: Square | Circle;
};
const shape: Shape = {
    type: ShapeType.Square,
    data: {
        // 即使 type 指定了 Sqare,这里仍允许出现 radius,而我们往往希望只允许出现 size
    },
};

然后给出 | 解决方案:

enum ShapeType {
    Square = 1,
    Circle = 2
}
interface Square {
    size: number;
}
interface Circle {
    radius: number;
}
// type Shape = {
//     type: ShapeType;
//     data: Square | Circle;
// };

type Shape = {
    [Type in ShapeType]: {
        type: Type,
        data:
            Type extends ShapeType.Square ?
                Square :
                Type extends ShapeType.Circle ?
                    Circle :
                    never;
    };
}[ShapeType];

const circle: Shape = {
    type: ShapeType.Circle,
    data: {
        // 🌟现在这里就只出现了 radius 类型
    },
};
const square: Shape = {
    type: ShapeType.Square,
    data: {
        // 🌟现在这里就只出现了 size 类型
    },
};

接着使用类型参数,更优雅地解决需求:

enum ShapeType {
    Square = 1,
    Circle = 2
}
interface Square {
    size: number;
}
interface Circle {
    radius: number;
}
// type Shape = {
//     type: ShapeType;
//     data: Square | Circle;
// };

// type Shape = {
//     [Type in ShapeType]: {
//         type: Type,
//         data:
//             Type extends ShapeType.Square ?
//                 Square :
//                 Type extends ShapeType.Circle ?
//                     Circle :
//                     never;
//     };
// }[ShapeType];

type Shape = {
    [Type in ShapeType]: {
        type: Type,
        data: {
            [ShapeType.Square]: Square;
            [ShapeType.Circle]: Circle;
        }[Type];
    };
}[ShapeType];


const circle: Shape = {
    type: ShapeType.Circle,
    data: {
        // ✨现在这里就只出现了 radius 类型
    },
};
const square: Shape = {
    type: ShapeType.Square,
    data: {
        // ✨现在这里就只出现了 size 类型
    },
};

同时还进行举一反三,实现让字段类型依赖 同级 的另一个字段

enum ShapeType {
    Square = 1,
    Circle = 2
}
interface Square {
    size: number;
}
interface Circle {
    radius: number;
}
type Shape = {
    [Type in ShapeType]: {
        type: Type,
    } & {
        [ShapeType.Square]: Square;
        [ShapeType.Circle]: Circle;
    }[Type];
}[ShapeType];

const circle: Shape = {
    type: ShapeType.Circle,
    // 这里只出现 radius
};
const square: Shape = {
    type: ShapeType.Square,
    // 这里只出现 size
};

最后给出完美解决方案,封装成一个可复用的工具类型:

enum ShapeType {
    Square = 1,
    Circle = 2
}
interface Square {
    size: number;
}
interface Circle {
    radius: number;
}

type MutableRecord<U> = {
    [SubType in keyof U]: {
        type: SubType;
        data: U[SubType]
    };
}[keyof U];

type Shape = MutableRecord<{
    [ShapeType.Square]: Square;
    [ShapeType.Circle]: Circle;
}>;


const circle: Shape = {
    type: ShapeType.Circle,
    data: {
        // 这里只出现 radius
    }
};
const square: Shape = {
    type: ShapeType.Square,
    data: {
        // 这里只出现 size
    }
};

他人案例二,一般,可读性不好,有关 infer

type Test = {
    key: "id";
    id: number;
} | {
    key: "action";
    action: string;
}
const test: Test = {
    key: 'id',
    // 只出现 id,类型为 number
}

const test2: Test = {
    key: 'action',
    // 只出现 action,类型为 string
}

type IData = {
    id: number;
    action: string;
};

type ITest<T> = keyof T extends infer U ? U extends keyof T ? { key: U, value: T[U] } : never : never


const test1: ITest<IData> = {
    key: 'id',
    value: // 类型提示数字
}
const test2: ITest<IData> = {
    key: 'action',
    value: // 类型提示字符串
}

解决我的实际需求

我的需求是,一份问卷中有多种类型的问题,比如主观题,单项选择题,那么不同类型的问题肯定对应不同的内容格式。

下面是抽离后的简单代码,麻雀虽小肝胆俱全:

enum SurveyQuestionType {
    INPUT_CONTENT = 'input_content',
    SINGLE_SELECT = "single_select"
}

type SurveyQuestion = {
    [Type in SurveyQuestionType]: {
        order: number,
        questionType: Type,
        questionContent: {
            [SurveyQuestionType.INPUT_CONTENT]: InputContent,
            [SurveyQuestionType.SINGLE_SELECT]: SingleContent
        }[Type]
    };
}[SurveyQuestionType];

type InputContent = {
    title: string,
}
type SingleContent = {
    titles: string[],
    options: string[]
}
const survey: SurveyQuestion[] = [
    {
        order: 1,
        questionType: SurveyQuestionType.INPUT_CONTENT,
        questionContent: { title: '问题一:xxxx' }
    },
    {
        order: 2,
        questionType: SurveyQuestionType.SINGLE_SELECT,
        questionContent: {
            titles: ['问题二:xxx', '问题三:xxxx'],
            options: ['A: xx', 'B: xx', 'C: xx', 'D: xx']
        }
    }
]


参考文章:

  • 8
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值