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

Ben*_*n M 9 generics partial typescript

如何创建一个还挺 - 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
Run Code Online (Sandbox Code Playgroud)

我要寻找的类型应该是:

  • 只接受通用类型上存在的键(就像普通一样Partial<T>
  • 接受通用类型的键的子集
  • 不接受undefined,如果泛型类型不接受undefined该键

这可能吗?


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

jca*_*alz 13

它是一种已知的限制在于打字稿不正确地被对象属性(和功能参数)之间进行区分缺少从那些与是存在的,但undefinedPartial<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 };
}
Run Code Online (Sandbox Code Playgroud)

所以你不能KindaPartial<T>直接写。但是,您可以编写一个类型VerifyKindaPartial<T, KP>,该类型 接受要与您的预期对象核对的类型T候选类型。如果候选人匹配,则返回匹配的内容。否则,它将返回不包含的内容。 KPKindaPartial<T>KP

然后,您可以创建merge()一个通用函数,KP该函数根据传入的值的类型进行推断value2。如果KP & VerifyKindaPartial<MyType, KP>匹配KP(表示KP匹配KindaPartial<MyType>),则代码将编译。否则,如果KP & VerifyKindaPartial<MyType, KP>匹配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', 
Run Code Online (Sandbox Code Playgroud)

这具有您想要的行为...尽管您得到的错误有点怪异(理想情况下,它会说这undefined不可分配给string,但是问题是编译器知道传入的类型为undefined,并且它希望该类型为是string的,所以编译器相交这些来undefined & stringnever。哦。

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

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


kim*_*ula 8

在这种情况下,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
Run Code Online (Sandbox Code Playgroud)

游乐场链接