反转必需和可选属性

Rem*_*ing 6 typescript

假设我有以下类型:

interface Foo {
  a?: string;
  b: string;
}
Run Code Online (Sandbox Code Playgroud)

可以使用设置所有属性Required

type Bar = Required<Foo>;

// Equivalent to
interface Bar {
  a: string;
  b: string;
}
Run Code Online (Sandbox Code Playgroud)

可以使用将所有属性设为可选Partial

type Bar = Partial<Foo>;

// Equivalent to
interface Bar {
  a?: string;
  b?: string;
}
Run Code Online (Sandbox Code Playgroud)

我想做一个通用类型,而不是翻转所有属性的可选标志。

type Bar = Flip<Foo>;

// Equivalent to
interface Bar {
  a: string;
  b?: string;
}
Run Code Online (Sandbox Code Playgroud)

为实现PartialRequired相当简单明了,但这种Flip类型需要知道哪些属性Foo是可选的,这是必需的。是否可以使用泛型读取此修改内容?

一些背景:我相信这defaultProps在React 中将是有用的类型。

jca*_*alz 5

对于您所说的问题(其中Foo没有索引签名),您可以这样定义Flip(我称之为FlipOptional):

type OptionalKeys<T> = {
  [K in keyof T]-?: {} extends Pick<T, K> ? K : never
}[keyof T];

type FlipOptional<T> = (Required<Pick<T, OptionalKeys<T>>> &
  Partial<Omit<T, OptionalKeys<T>>>) extends infer O
  ? { [K in keyof O]: O[K] }
  : never;
Run Code Online (Sandbox Code Playgroud)

首先,我们必须确定的哪些键T是可选的。没有简单的内置方法可以执行此操作,但是上面的OptionalKeys类型别名可以通过检查哪些单属性扩展T弱类型来工作,如对相关问题的回答所述。索引签名将这一点弄乱了,因此,如果您需要对具有索引签名的类型进行操作,则需要更复杂的东西(在链接的答案中也有介绍)。

一旦确定了哪些键是可选的,就可以轻松地构造一个与您想要的键等效的类型,PickOmitPartialRequired以及一个和一个交集:Required<Pick<T, OptionalKeys<T>>> & Partial<Omit<T, OptionalKeys<T>>>。唯一的问题是这种类型相对难看。如果将类型写为单个对象类型,那就更好了。

在这里,我使用以下形式的带条件类型推断的技巧。假设我们有一个Ugly包含许多交集和映射的类型。为了使它漂亮,我们可以做

type Pretty = Ugly extends infer O ? {[K in keyof O]: O[K]} : never
Run Code Online (Sandbox Code Playgroud)

这里,Ugly本质上是“复制”到O参数,该参数被映射而不更改任何内容。就输出而言,这基本上是无操作的,但是如果可能的话,结果将是单个对象类型。


让我们尝试一下:

interface Foo {
  a?: string;
  b: string;
}

type Bar = FlipOptional<Foo>;
/* type Bar = {
    a: string;
    b?: string | undefined;
} */

type FooAgain = FlipOptional<Bar>;
/* type FooAgain =  {
    b: string;
    a?: string | undefined;
} */

type MutuallyAssignable<T extends U, U extends V, V = T> = true;
type FooAgainIsFoo = MutuallyAssignable<Foo, FooAgain>; // okay
Run Code Online (Sandbox Code Playgroud)

这一切看起来都不错。 Bar是您所期望的,并FlipOptional<Bar>提供了等效于的类型Foo


好的,希望对您有所帮助。祝好运!

链接到代码