我想在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
答案 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]
将保持不变,并且可能未定义。
通过这种方式,我们有效地绕过了-?
的副作用,并且结果类型完全符合我的要求。