required 是 typescript 内置映射类型,将类型中所有可选属性立即转为必填,其他属性保持不变;其实现为 { [p in keyof t]-?: t[p] },仅处理第一层,不递归深层嵌套。

Required 是 TypeScript 内置的映射工具类型,作用就是把一个类型中所有可选属性(带 ? 的)**立刻转为必填**,其他属性(原本就必填的)保持不变。它不新增、不删减字段,只统一“去可选性”。
核心机制:用 -? 修饰符批量移除可选标记
它的实现本质是遍历类型所有键,并对每个属性应用 -? 映射修饰符——这个符号明确表示“去掉可选性”。源码等价于:
注意两点:
-
-?不影响原本就是必填的属性,只作用于带?的字段; - 它只处理第一层,深层嵌套对象里的可选属性不会被自动递归处理。
实际用法:直接包裹原类型即可
比如你有这样一个接口:
interface User {id: number;
name: string;
email?: string;
avatar?: string;
}
只需写 Required<user></user>,就能得到完全等效于:
id: number;
name: string;
email: string;
avatar: string;
}
此时赋值时必须提供 email 和 avatar,否则编译报错。
深层嵌套需自定义 DeepRequired
如果类型里有嵌套对象,例如:
interface Profile {name: string;
contact?: { phone?: string; email?: string };
}
直接用 Required<profile></profile> 只会让 contact 变成必填,但 contact.phone 和 contact.email 仍可选。要彻底“层层必填”,得手写递归类型:
[K in keyof T]-?: T[K] extends object ? DeepRequired
};
这样 DeepRequired<profile></profile> 就会强制 contact、phone、email 全部存在。
和 Pick + -? 的区别
有人尝试用 Pick<t keyof t></t> 再加 -?,但这是多余操作。Required 本身就是专为此场景设计的开箱即用工具,无需组合其他类型。它简洁、语义清晰、TypeScript 官方维护,出错率低。











