没有未定义的 TypeScript Partial<T> 类型
Posted
技术标签:
【中文标题】没有未定义的 TypeScript Partial<T> 类型【英文标题】:TypeScript Partial<T> type without undefined 【发布时间】:2019-02-02 03:51:46 【问题描述】:如何创建一个 kinda-Partial<T>
类型,不允许 undefined
值?
这是一个例子:
interface MyType
foo: string
bar?: number
const merge = (value1: MyType, value2: KindaPartial<MyType>): MyType =>
return ...value1, ...value2;
const value =
foo: 'foo',
bar: 42
merge(value, ); // should work
merge(value, foo: 'bar' ); // should work
merge(value, bar: undefined ); // should work
merge(value, bar: 666 ); // should work
merge(value, foo: '', bar: undefined ); // should work
merge(value, foo: '', bar: 666 ); // should work
// now the problematic case:
merge(value, foo: undefined ); // this should throw an error
// because MyType["foo"] is of type string
我正在寻找的类型应该:
只接受存在于泛型类型上的键(就像普通的Partial<T>
)
接受泛型键的子集
但如果泛型类型不接受 undefined
作为该键,则不接受 undefined
这可能吗?
编辑:我还在 TypeScript 存储库中创建了一个问题,因为这很奇怪,应该在某个时候抛出错误:https://github.com/Microsoft/TypeScript/issues/29701
【问题讨论】:
请记住,这样做可能会使您的代码更难以编程方式使用;你会禁止例如update(value: MyType, new_foo_val?: string) return merge(value, foo: new_foo_val )
.
【参考方案1】:
TS 4.4 更新:
TS4.4 将具有 an --exactOptionalPropertyTypes
compiler flag 以直接使用 Partial
为您提供您正在寻找的行为,只要您有意在您希望允许的位置添加 undefined
:
interface MyType
foo: string
bar?: number | undefined // <-- you want this
const merge = (value1: MyType, value2: Partial<MyType>): MyType =>
return ...value1, ...value2 ;
const value =
foo: 'foo',
bar: 42
merge(value, ); // okay
merge(value, foo: 'bar' ); // okay
merge(value, bar: undefined ); // okay
merge(value, bar: 666 ); // okay
merge(value, foo: '', bar: undefined ); // okay
merge(value, foo: '', bar: 666 ); // okay
// now the problematic case:
merge(value, foo: undefined ); // error!
// ----------> ~~~
// Type 'undefined' is not assignable to type 'string'
Playground link to code --exactOptionalPropertyTypes;由于某种原因,网址已损坏
——--
PRE-TS4.4 答案:
TypeScript 无法正确区分 缺少 的对象属性(和函数参数)与 存在但 @987654332 的对象属性(和函数参数)是一个已知限制(参见 microsoft/TypeScript#13195) @。 Partial<T>
允许 undefined
属性的事实是其结果。正确的做法是等到此问题得到解决(如果您在 GitHub 中访问该问题并给它一个 ? 或带有令人信服的用例的评论,这可能会变得更有可能)。
如果您不想等待,您可以使用以下 hacky 方式来获得类似这种行为:
type VerifyKindaPartial<T, KP> =
Partial<T> & [K in keyof KP]-?: K extends keyof T ? T[K] : never;
const merge = <KP>(value1: MyType, value2: KP & VerifyKindaPartial<MyType, KP>): MyType =>
return ...value1, ...value2 ;
所以你不能直接写KindaPartial<T>
。但是您可以编写一个类型 VerifyKindaPartial<T, KP>
,它接受一个类型 T
和一个 candidate 类型 KP
,您想检查您想要的 KindaPartial<T>
。如果候选匹配,则返回匹配KP
的内容。否则它会返回不存在的东西。
然后,您将merge()
设为generic 函数,该函数从传递给value2
的值的类型推断KP
。如果KP & VerifyKindaPartial<MyType, KP>
匹配KP
(意味着KP
匹配KindaPartial<MyType>
),则代码将编译。否则,如果KP & VerifyKindaPartial<MyType, KP>
not 匹配KP
(意味着KP
不匹配KindaPartial<MyType>
),则会出现错误。 (不过,错误可能不是很直观)。
让我们看看:
merge(value, ); // works
merge(value, foo: 'bar' ); // works
merge(value, bar: undefined ); // works
merge(value, bar: 666 ); // works
merge(value, foo: '', bar: undefined ); // works
merge(value, foo: '', bar: 666 ); // works
merge(value, foo: undefined ); // error!
// ~~~ <-- undefined is not assignable to never
// the expected type comes from property 'foo',
这有你想要的行为......虽然你得到的错误有点奇怪(理想情况下它会说undefined
不能分配给string
,但问题是编译器知道传入的类型是undefined
,它希望类型是string
,所以编译器将这些相交到undefined & string
,即never
。哦,好吧。
无论如何,这里可能有一些警告;泛型函数在直接调用时运行良好,但由于 TypeScript 对更高种类的类型的支持不是很好,所以它们不能很好地组合。我不知道这是否真的适用于您的用例,但这是我能用目前的语言做的最好的事情。
Playground link to code
【讨论】:
感谢您的解释!我对这个问题竖起了大拇指。自从 TypeScript 和 Partial[P in K]: X
对K
的每个联合成员都有一个属性。
您的KindaMyType
与MyType
的类型相同。您可以(不安全地)使用类型断言(您称之为“强制转换”)将bar: 666
缩小到MyType
这一事实与接受部分MyType
的函数不同。我很惊讶你没有在你的 cmets 中提到类型断言,因为这是你解决方法的关键。【参考方案2】:
在这种情况下,Pick
应该可以工作。
interface MyType
foo: string
bar?: number
const merge = <K extends keyof MyType>(value1: MyType, value2: Pick<MyType, K>): MyType =>
return ...value1, ...value2;
merge(value, ); // ok
merge(value, foo: 'bar' ); // ok
merge(value, bar: undefined ); // ok
merge(value, bar: 666 ); // ok
merge(value, foo: '', bar: undefined ); // ok
merge(value, foo: '', bar: 666 ); // ok
merge(value, foo: undefined ); // ng
Playground link
【讨论】:
以上是关于没有未定义的 TypeScript Partial<T> 类型的主要内容,如果未能解决你的问题,请参考以下文章
在 TypeScript 中,当类型是函数的参数时,有没有办法限制 Partial<T> 类型的额外/多余属性?
当没有要指定的确切值“未定义”或“空”时,为 TypeScript 可选参数传递啥? [复制]
Typescript的高级tricks(in,keyof,Partial,Pick,Exclude等)
typescript-eslint 配置:.eslintrc 文件“模块”未定义