typeScript允许您使用-?
映射类型修饰符删除'Roptionability'(?)
,并易于与Required<T>
类型一起使用。
type Required<T> = { [P in keyof T]-?: T[P] };
但是,使用--strictNullChecks
(我是(时要注意以下内容。
请注意,当同构映射类型时,在 - strictnullchecks模式 删除一个?从基础类型中的属性中的修饰符也 从该属性的类型中删除未定义:
我正在寻找一种绕过此副作用的方法...
即。我想删除?
,但是如果存在|undefined
,我想保留它
为什么?
我具有具有可选(?
(属性的服务器生成的接口。我想重构服务器代码并添加/删除成员。因此,我想要一种方法,以便我被迫明确设置一个值(或明确设置为undefined
(的每个属性,无论是否需要。
困境:
- 如果我使用
Required<T>
,则它会吞下我的| undefined
,但前提
因此,具有讽刺意味的是:
export type RequiredDog = Required <{
bark: 'loud' | 'quiet' | undefined,
bite?: 'nip' | 'clamp' | undefined
}>;
实际上变成了:
type RequiredDog = {
bark: "loud" | "quiet" | undefined;
bite: "nip" | "clamp";
}
SO bite
比bark
更可选的CC_13现在实际上更少可选!
是否可以在不删除undefined
的情况下删除?
?
如果我试图实现不剥离属性的undefined
的Required<>
,我可能会这样做:
type RequiredKeepUndefined<T> = { [K in keyof T]-?: [T[K]] } extends infer U
? U extends Record<keyof U, [any]> ? { [K in keyof U]: U[K][0] } : never
: never;
我在这里使用一堆映射的类型和有条件的类型将值类型包裹在一个ing中,使其成为Required
,然后解开一键。这在精神上与您的答案相似,但是如果某人使用"undefined"
作为属性字符串的字符串,则不会发生什么。
您可以看到它的表现如下:
interface Dog {
bark: "loud" | "quiet" | undefined;
bite?: "nip" | "clamp" | undefined;
}
type RequiredKeepUndefinedDog = RequiredKeepUndefined<Dog>
/* type RequiredKeepUndefinedDog = {
bark: "loud" | "quiet" | undefined;
bite: "nip" | "clamp" | undefined;
} */
您可以将其视为以下转换
-
{bark: "loud" | "quiet" | undefined, bite?: "nip" | "clamp" | undefined}
-
{bark: ["loud" | "quiet" | undefined], bite?: ["nip" | "clamp" | undefined] | undefined}
-
{bark: ["loud" | "quiet" | undefined], bite: ["nip" | "clamp" | undefined]}
-
{bark: "loud" | "quiet" | undefined, bite: "nip" | "clamp" | undefined}
好吧,希望有帮助;祝你好运!
链接到代码
我仍然会喜欢一个更清洁的解决方案,但是以下内容似乎有效:
我基本上创建了一种称为 'undefined'
的类型,我用这两个替代/unsubStitute将真实的undefined
类型替换为:
type WrapUndefined<T> = {
[P in keyof T]: undefined extends T[P] ? 'undefined' | T[P] : T[P];
};
type UnwrapUndefined<T> = {
[P in keyof T]: 'undefined' extends T[P] ? Diff<T[P], 'undefined'> | undefined : T[P];
};
所以如果我有:
type Person = {
firstName?: string;
}
然后 WrapUndefined<Person>
使它
firstName?: string | undefined | 'undefined'; // *
然后,您可以在此上调用Required<T>
以获取:
firstName: string | 'undefined'; // 'undefined' doesn't get removed now
然后用UnwrapUndefined<T>
将其反向
firstName: string | undefined;
因此,我创建了一个类型来完成这一切:
export type SmartRequired<T> = UnwrapUndefined<Required<WrapUndefined<T>>>;
如果我从我的原始Dog
类型上运行此问题,我会得到我想要的:
export type RequiredDog = SmartRequired <{
bark: 'loud' | 'quiet' | undefined,
bite?: 'nip' | 'clamp' | undefined
}>;
这是这个
type RequiredDog = {
bark: "loud" | "quiet" | undefined;
bite: "nip" | "clamp" | undefined;
}
*高级注:此步骤在VSCODE中显示为string | undefined
,因为它似乎正在吸收"未定义"中的string
中。幸运的是,当您运行所有三个步骤时,它以正确的结果结束。
这是@Simon_Weaver答案的重构。
interface Dog {
bark: "loud" | "quiet" | undefined;
bite?: "nip" | "clamp" | undefined;
howl?: "short" | "long";
}
type RequiredDog = Required<Dog>
/* type RequiredDog = {
bark: "loud" | "quiet" | undefined;
bite: "nip" | "clamp";
howl: "short" | "long";
} */
declare const Undefined: unique symbol;
type Undefined = typeof Undefined;
type Mark<T> = undefined extends T ? Exclude<T, undefined> | Undefined : T;
type Unmark<T> = Undefined extends T ? Exclude<T, Undefined> | undefined : T;
type MarkProps<O> = { [P in keyof O]: Mark<O[P]> };
type UnmarkProps<O> = { [P in keyof O]: Unmark<O[P]> };
type RequiredKeepUndefined<O> = UnmarkProps<Required<MarkProps<O>>>;
type RequiredKeepUndefinedDog = RequiredKeepUndefined<Dog>
/* type RequiredKeepUndefinedDog = {
bark: "loud" | "quiet" | undefined;
bite: "nip" | "clamp" | undefined;
howl: "stort" | "long" | undefined;
} */
使用唯一的符号类型标记undefined
S使其免受碰撞的安全。
将其分解成更多的帮助者,使其自我解释。
游乐场
这是我们使用的:
type RequiredWithUnknown<T> = {
[K in keyof Required<T>]: T[K]
}
ts游乐场
应该很容易理解。但简而言之:
该类型正在获取T
的所有键K
。由于Required<T>
,即使它们是可选的,它也确实可以获得所有键。然后用这些必需的键从t取值。
它仅适用于第一级。嵌套的对象未被触摸。