没有未定义的TypeScript Partial <t>类型

时间:2019-02-02 03:51:47

标签: typescript generics partial

如何创建不允许使用 <?php //logout.php session_start(); [code here to say] UPDATE registrants SET voted='1' WHERE id=:id session_destroy(); header("location:thanks.php"); ?> 值的 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

这可能吗?


编辑:我还在TypeScript存储库中创建了一个问题,因为这很奇怪,并且在某个时候会引发错误:https://github.com/Microsoft/TypeScript/issues/29701

2 个答案:

答案 0 :(得分:6)

这是known limitation,TypeScript不能正确区分缺少的对象属性(和函数参数)与存在undefined的对象属性(和函数参数) Partial<T>允许undefined属性的事实是其结果。正确的做法是等到解决此问题为止(如果您在GitHub上处理该问题并给它一个或一个令人信服的用例发表评论,则可能会出现这种情况。)

如果您不想等待,则可以使用以下黑客方式获得类似的行为:

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类型和一个候选类型KP,并要与预期的KindaPartial<T>进行比较。如果候选人匹配,则返回匹配KP的内容。否则,它将返回不包含的内容。

然后,将merge()设为generic函数,该函数根据传递给KP的值的类型来推断value2。如果KP & VerifyKindaPartial<MyType, KP>KP匹配(意味着KPKindaPartial<MyType>匹配),则代码将编译。否则,如果KP & VerifyKindaPartial<MyType, KP> KP不匹配(意味着KPKindaPartial<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的{​​{1}}相交。哦。

反正有可能这里的警告;泛型函数在直接调用时可以很好地工作,但它们的组合效果不好,因为TypeScript对种类较多的类型的支持不是那么好。我不知道这是否真的适合您的用例,但这是我目前能用的最好的语言。

希望有所帮助;祝你好运!

答案 1 :(得分:6)

在这种情况下,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