Проблема, с которой вы сталкиваетесь, заключается в том, что компилятор имеет тенденцию расширять кортежи для массивов, а литералы - для нелитералов, если вы не дадите ему конкретные подсказки. абонент из del()
может использовать утверждение as const
в параметре path
, чтобы получить этот эффект (хотя вам нужно будет принять readonly string[]
, а не только string[]
), но вы по-видимому, хотел бы, чтобы абонент не беспокоился об этом. Я подал microsoft / TypeScript # 30680 , чтобы попросить что-то вроде as const
в подписи вызова, чтобы сделать это. При отсутствии такой функции вы должны полагаться на некоторые странные подсказки.
Если вы хотите, чтобы тип массива выводился как кортеж, если это возможно, вы должны включить тип кортежа в его контекст, например, через объединение. Так T extends Foo[]
становится T extends Foo[] | []
. Если вы хотите, чтобы тип выводился как строковый литерал, если это возможно, вы должны включить строковый литерал в его контекст или какой-либо другой string
-подобный совет. Так что T extends string[]
становится либо T extends (string | (""&{__:0}))[]
, либо, по моему предпочтению, T extends N[]
, где N
- это еще один обобщенный c параметр N extends string
. Вот как это выглядит:
declare const del: <T extends object, K extends N[] | [], N extends string>(
src: T, path: K
) => DeepOmit<T, K>;
И вы можете увидеть, как оно работает так, как вы и планировали:
const deletedObj = del({ a: { b: { c: 1 } } }, ["a", "b", "c"]);
deletedObj.a.b; // okay
deletedObj.a.b.c; // error
Также обратите внимание, что ваше определение DeepOmit
излишне окольным; {0: X, 1: Y}[T extends U ? 0 : 1]
- это способ обойти ошибки, когда компилятор считает T extends U ? X : Y
круговым (даже если этот вид обхода не поддерживается ). Но простая реализация DeepOmit
не противоречит этим правилам: хорошо иметь рекурсивный тип, в котором рекурсия происходит в свойстве объекта:
type DeepOmit<T, Path extends string[]> = T extends object ?
Path['length'] extends 1 ? Omit<T, Path[0]> : {
[K in keyof T]: K extends Path[0] ? DeepOmit<T[K], Tail<Path>> : T[K];
} : T; // no error, still works
Хорошо, надеюсь что помогает; удачи!
Детская площадка ссылка на код