使用时如何防止<T>打字稿中的"必需"从类型中删除'undefined' --strictNullChecks



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 bitebark更可选的CC_13现在实际上更少可选!

是否可以在不删除undefined的情况下删除?

如果我试图实现不剥离属性的undefinedRequired<>,我可能会这样做:

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取值。

它仅适用于第一级。嵌套的对象未被触摸。

相关内容

  • 没有找到相关文章

最新更新