You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在TypeScript接口中用首个属性值定义第二个属性?含Zod方案

TypeScript 类型实现方案

你遇到的错误是映射类型不能声明属性或方法,原因是映射类型语法中不能直接混合固定属性(比如type: T)和映射字段。可以通过以下两种方式解决:

方式一:交叉类型组合

先定义固定的type属性,再和映射生成的字段做交叉:

type IconType = 'file' | 'emoji' | 'external';

type IconFile<T extends IconType = IconType> = { type: T } & { [K in T]: string };

// 合法示例
const a: IconFile<'file'> = { type: 'file', file: 'foo' };
const b: IconFile<'emoji'> = { type: 'emoji', emoji: 'bar' };
const c: IconFile<'external'> = { type: 'external', external: 'baz' };

// 不指定泛型时,自动兼容所有合法组合
const d: IconFile = { type: 'file', file: 'test' };

方式二:分布式条件类型约束

通过索引访问生成严格的联合类型,确保每个type只对应专属字段:

type IconType = 'file' | 'emoji' | 'external';

type IconFile = {
  [T in IconType]: { type: T } & Record<T, string>
}[IconType];

// 合法示例
const a: IconFile = { type: 'file', file: 'foo' };
// 错误示例:type为file时不能出现emoji字段
const wrong: IconFile = { type: 'file', emoji: 'bar' };
TypeScript + Zod 实现方案

用Zod的discriminatedUnion可以同时实现编译时类型约束和运行时数据校验:

import { z } from 'zod';

// 定义各类型的子校验规则
const fileIconSchema = z.object({
  type: z.literal('file'),
  file: z.string(),
});

const emojiIconSchema = z.object({
  type: z.literal('emoji'),
  emoji: z.string(),
});

const externalIconSchema = z.object({
  type: z.literal('external'),
  external: z.string(),
});

// 基于type字段创建联合校验器
const iconSchema = z.discriminatedUnion('type', [
  fileIconSchema,
  emojiIconSchema,
  externalIconSchema,
]);

// 自动推导TypeScript类型
type IconFile = z.infer<typeof iconSchema>;

// 校验测试
iconSchema.parse({ type: 'file', file: 'foo' }); // 成功
iconSchema.parse({ type: 'emoji', emoji: 'bar' }); // 成功
iconSchema.parse({ type: 'external', external: 'baz' }); // 成功
iconSchema.parse({ type: 'file', emoji: 'bar' }); // 抛出校验错误

内容的提问来源于stack exchange,提问作者yuusheng

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 03:24:57