必需但可能未定义的类型

时间:2018-10-24 12:13:40

标签: 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 }

所需属性:

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

1 个答案:

答案 0 :(得分:1)

我终于找到了解决方案。而且它比我预期的更具可读性。

type Required_ish<T> =
{
    [K in keyof Required<T>]: T[K]
};

好,但是为什么行得通呢?让我们来分解一下。

首先,我从同形mapped type开始:

type Required_ish<T> = { [K in keyof T]: T[K] }

映射类型是一项功能,允许您修改类型但保持其内部结构(元组仍将是元组)和属性修饰符(只读,可选),除非您明确指出。我刚写的那行差不多是一种身份类型:对于T的每个键,它返回的类型和修饰符与T相同。

现在有两个修饰符可以更改属性的要求:

{ [K in keyof T]?:  T[K] } // add the optional modifier
{ [K in keyof T]-?: T[K] } // remove the optional modifier

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

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

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