必需但可能未定义的类型

m93*_*93a 7 typescript

我想在 TypeScript 中创建一个类,它以一种要求具有该接口的所有属性(甚至是可选属性)的方式实现一个接口,但允许可选属性为undefined.

该Required_ish<T>类型比 更宽Required<T>,但比 更严格T,因为它需要我显式列出所有属性。Required_ish<T>然而,相同的值可以分配给和的参数T。

我已经尝试过,但它似乎做了同样的事情Required:

type Required_ish<T> = T & { [K in keyof T]-?: T[K] | undefined }
Run Code Online (Sandbox Code Playgroud)

所需的属性:

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

class Bar1 implements Foo               { a = ''; } // allowed
class Bar2 implements Required_ish<Foo> { a = ''; } // error
class Bar3 implements Required<Foo>     { a = ''; } // error

class Bar4 implements Foo               { a = ''; b = undefined; } // allowed
class Bar5 implements Required_ish<Foo> { a = ''; b = undefined; } // allowed
class Bar6 implements Required<Foo>     { a = ''; b = undefined; } // error

class Bar7 implements Foo               { a = ''; b = 0; } // allowed
class Bar8 implements Required_ish<Foo> { a = ''; b = 0; } // allowed
class Bar9 implements Required<Foo>     { a = ''; b = 0; } // allowed
Run Code Online (Sandbox Code Playgroud)

m93*_*93a 9

我终于找到了解决办法。它比我预期的更具可读性。

type Required_ish<T> =
{
    [K in keyof Required<T>]: T[K]
};
Run Code Online (Sandbox Code Playgroud)

好的,但是为什么这会起作用呢?让我们来分解一下。

首先,我从同态映射类型开始:

type Required_ish<T> = { [K in keyof T]: T[K] }
Run Code Online (Sandbox Code Playgroud)

映射类型是一项功能,允许您修改类型但保留其内部结构(元组仍然是元组)和属性修饰符(只读、可选),除非您明确说明它。我刚刚写的行或多或少是一个身份类型:它的每个键都T返回与之前相同的类型和修饰符T。

现在有两个修饰符允许更改属性的必需性:

{ [K in keyof T]?:  T[K] } // add the optional modifier
{ [K in keyof T]-?: T[K] } // remove the optional modifier
Run Code Online (Sandbox Code Playgroud)

在这里使用可能很诱人-?,但遗憾的是这个修饰符做了两件事。它不仅从属性中删除了可选修饰符,还从结果类型中删除了 undefined。这意味着每当您使用 时-?,您都无法返回扩展未定义的类型。

幸运的是,我们不是可以从 获取密钥T,而是可以从 获取密钥Required<T>。由于编译器从左侧( 之前的部分:)获取所有修饰符,因此该属性将具有与 相同的修饰符Required<T>[K]。这意味着它永远不会是可选的,并且readonly修饰符将保持不变。因为我们没有使用修饰符-?,T[K]所以将保持完整并且可能未定义。

通过这种方式,我们有效地绕过了副作用,-?并且生成的类型完全符合我的要求。

  • 请提供说明为什么此代码是可能的解决方案。 (5认同)