TypeScript Частичный <T>тип без неопределенного - PullRequest
0 голосов
/ 02 февраля 2019

Как создать тип своего рода - 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

1 Ответ

0 голосов
/ 02 февраля 2019

Это известное ограничение , что 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() универсальную функцию, которая выводит 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', 

Такое поведение вы хотите ... хотя ошибка, которую вы получаете, немного странная (в идеалебыло бы сказано, что undefined нельзя присвоить string, но проблема в том, что компилятор знает, что переданный тип - undefined, и он хочет, чтобы тип был string, поэтому компилятор пересекает ихundefined & string что составляет never. Хорошо.

В любом случае, здесь есть, вероятно, предостережения: универсальные функции хорошо работают при непосредственном вызове, но они плохо сочетаются, потому что TypeScript не поддерживает более старшие типы.это хорошо. Я не знаю, будет ли это на самом деле работать для вашего случая использования, но это лучшее, что я могу сделать с языком, каким он является в настоящее время.

Надеюсь, это поможет; удачи!

...