Я обнаружил загадочную ошибку, скажем, я хочу иметь довольно бесполезную функцию, которая объединяет две дополняющие части объекта:
function foo<T extends object, K extends keyof T>(a: Pick<T, K>, b: Omit<T, K>): T {
return { ...a, ...b } // TS2322: Type 'Pick<T, K> & Omit<T, K>' is not assignable to type 'T'. 'Pick<T, K> & Omit<T, K>' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'object'.
}
Я получил ошибку выше, в то время как код ниже явно работает:
interface Test {
a: string,
b: string
}
const a: Pick<Test, 'a'> = { a: 'a' }
const b: Omit<Test, 'a'> = { b: 'b' }
const t: Test = { ...a, ...b }
Редактировать 1:
function foo<T extends object, K extends keyof T>(a: Pick<T, K>, b: Omit<T, K>): T extends Pick<T, K> & Omit<T, K> ? T : any {
const res: Pick<T, K> & Omit<T, K> = { ...a, ...b }
return res // TS2322: Type 'Pick<T, K> & Omit<T, K>' is not assignable to type 'T extends Pick<T, K> & Omit<T, K> ? T : any'.
}
Редактировать 2:
function foo<T, K extends keyof T>(x: Pick<T, K> & Omit<T, K>): T {
return x // TS2322: Type 'Pick<T, K> & Omit<T, K>' is not assignable to type 'T'. 'T' could be instantiated with an arbitrary type which could be unrelated to 'Pick<T, K> & Omit<T, K>'.
}
function bar<T, K extends keyof T>(x: T): Pick<T, K> & Omit<T, K> {
return x // no issue
}
Здесь действительно есть вероятность несостоятельности. И Pick, и Omit реализованы как сопоставленные типы, и их результат может не содержать всех характеристик исходного типа.
Учти это:
interface Test {
(arg: string): string
a: string,
b: string
}
const a: Pick<Test, 'a'> = { a: 'a' }
const b: Omit<Test, 'a'> = { b: 'b' }
const t: Test = { ...a, ...b }
// ~ Type '{ b: string; a: string; }' provides no match
// for the signature '(arg: string): string'
Есть и еще одна проблема: компилятору не хватает возможности правильно проанализировать результат таких операций, как Pick<T, K>, когда T и K являются обобщенными типами. Типы T и K больше похожи на заполнители. Их конкретный тип неизвестен до тех пор, пока не будет вызвана функция, поэтому компилятор имеет лишь ограниченное представление о том, что будет означать пересечение Pick<T, K> с Omit<T, K>. Обсуждение похожей проблемы можно увидеть здесь по адресу #28884.
Детская площадка
Это когда вы застреваете в сорняках машинописного текста вместо того, чтобы выполнять работу.