Дополняя @fetzz отличным ответом.
КОРОТКИЙ ОТВЕТ
TL; DR; Есть две распространенные причины сообщения об ошибке такого типа. Вы делаете первый (см. Ниже). Наряду с текстом я подробно объясняю, что хочет передать это сообщение об ошибке.
ПРИЧИНА 1. В машинописном тексте конкретный экземпляр не может быть назначен параметру типа. Ниже вы можете увидеть примеры «проблемы» и «решенной проблемы», чтобы вы могли сравнить разницу и увидеть, какие изменения:
ПРОБЛЕМА
const func1 = <A extends string>(a: A = 'foo') => `hello!` // Error!
const func2 = <A extends string>(a: A) => {
//stuff
a = `foo` // Error!
//stuff
}
РЕШЕНИЕ
const func1 = <A extends string>(a: A) => `hello!` // ok
const func2 = <A extends string>(a: A) => { //ok
//stuff
//stuff
}
См в: Площадка для TS
ПРИЧИНА 2: хотя в вашем коде не происходит ошибка, указанная ниже. Это также нормальная ситуация, когда появляется такое сообщение об ошибке. Вам следует избегать этого:
Повторить (по ошибке) Type Parameter
в классе, типе или интерфейсе.
Не позволяйте сложности приведенного ниже кода сбивать вас с толку, единственное, на чем я хочу, чтобы вы сосредоточились, - это то, как удаление буквы «А» решает проблему:
ПРОБЛЕМА:
type Foo<A> = {
//look the above 'A' is conflicting with the below 'A'
map: <A,B>(f: (_: A) => B) => Foo<B>
}
const makeFoo = <A>(a: A): Foo<A> => ({
map: f => makeFoo(f(a)) //error!
})
РЕШЕНИЕ:
type Foo<A> = {
// conflict removed
map: <B>(f: (_: A) => B) => Foo<B>
}
const makeFoo = <A>(a: A): Foo<A> => ({
map: f => makeFoo(f(a)) //ok
})
См: TS площадка а>
ДЛИННЫЙ ОТВЕТ
ПОНЯТИЕ СООБЩЕНИЯ ОБ ОШИБКЕ
Далее я разложу каждый элемент сообщения об ошибке ниже:
Type '{}' is not assignable to type 'P'.
'{}' is assignable to the constraint of type 'P', but 'P' could be
instantiated with a different subtype of constraint'object'
ЧТО ТАКОЕ ТИП {}
Это тип, которому вы можете присвоить что угодно, кроме null или undefined. Например:
type A = {}
const a0: A = undefined // error
const a1: A = null // error
const a2: A = 2 // ok
const a3: A = 'hello world' //ok
const a4: A = { foo: 'bar' } //ok
// and so on...
См: TS площадка EM>
ЧТО ТАКОЕ is not assignable
Назначить - значит сделать переменную определенного типа, соответствующей конкретному экземпляру. Если вы не соответствуете типу экземпляра, вы получите ошибку. Например:
// type string is not assignable to type number
const a: number = 'hello world' //error
// type number is assinable to type number
const b: number = 2 // ok
ЧТО ТАКОЕ different subtype
Два типа равны: если они не добавляют и не удаляют детали по отношению друг к другу.
Два типа разные: если они не равны.
Тип A
является подтипом типа S
: если A
добавляет деталь, без удаления уже существующей детали из S
.
тип A
и тип B
- разные подтипы типа S
: если A
и B
являются подтипами S
, но A
и B
- разные типы. Другими словами: A
и B
добавляют детали к типу S
, но они не добавляют одинаковых деталей.
Пример. В приведенном ниже коде верны все следующие утверждения:
- A и D - одинаковые типы
- B - подтип A
- E не подтип A
- B и C - разные подтипы A
type A = { readonly 0: '0'}
type B = { readonly 0: '0', readonly foo: 'foo'}
type C = { readonly 0: '0', readonly bar: 'bar'}
type D = { readonly 0: '0'}
type E = { readonly 1: '1', readonly bar: 'bar'}
type A = number
type B = 2
type C = 7
type D = number
type E = `hello world`
type A = boolean
type B = true
type C = false
type D = boolean
type E = number
ПРИМЕЧАНИЕ: Структурный тип
Когда вы видите в TS использование ключевого слова type
, например, в type A = { foo: 'Bar' }
, вы должны прочитать: Псевдоним типа A
указывает на структуру типа { foo: 'Bar' }
.
Общий синтаксис: type [type_alias_name] = [type_structure]
.
Система типов Typescript просто проверяет [type_structure]
, а не [type_alias_name]
. Это означает, что в TS нет разницы в проверке типов между следующими: type A = { foo: 'bar }
и type B = { foo: 'bar' }
. Для получения дополнительной информации см .: Официальный документ.
ЧТО ТАКОЕ constraint of type
'X'
Ограничение типа - это просто то, что вы помещаете справа от ключевого слова extends. В приведенном ниже примере Type Constraint
- это «B».
const func = <A extends B>(a: A) => `hello!`
Читает: Ограничение типа "B" - это constraint of type 'A'
ПОЧЕМУ ПРОИСХОДИТ ОШИБКА
Для иллюстрации я покажу вам три случая. Единственное, что будет меняться в каждом случае, - это Type Constraint
, больше ничего не изменится.
Я хочу, чтобы вы обратили внимание, что ограничение, которое Type Constraint
налагает на Type Parameter
, не включает различные подтипы. Давай увидим это:
Данный:
type Foo = { readonly 0: '0'}
type SubType = { readonly 0: '0', readonly a: 'a'}
type DiffSubType = { readonly 0: '0', readonly b: 'b'}
const foo: Foo = { 0: '0'}
const foo_SubType: SubType = { 0: '0', a: 'a' }
const foo_DiffSubType: DiffSubType = { 0: '0', b: 'b' }
СЛУЧАЙ 1: БЕЗ ОГРАНИЧЕНИЙ
const func = <A>(a: A) => `hello!`
// call examples
const c0 = func(undefined) // ok
const c1 = func(null) // ok
const c2 = func(() => undefined) // ok
const c3 = func(10) // ok
const c4 = func(`hi`) // ok
const c5 = func({}) //ok
const c6 = func(foo) // ok
const c7 = func(foo_SubType) //ok
const c8 = func(foo_DiffSubType) //ok
СЛУЧАЙ 2: НЕКОТОРЫЕ ОГРАНИЧЕНИЯ
Обратите внимание, что ограничение не влияет на подтипы.
ОЧЕНЬ ВАЖНО: в Typescript Type Constraint
не ограничивает разные подтипы
const func = <A extends Foo>(a: A) => `hello!`
// call examples
const c0 = func(undefined) // error
const c1 = func(null) // error
const c2 = func(() => undefined) // error
const c3 = func(10) // error
const c4 = func(`hi`) // error
const c5 = func({}) // error
const c6 = func(foo) // ok
const c7 = func(foo_SubType) // ok <-- Allowed
const c8 = func(foo_DiffSubType) // ok <-- Allowed
СЛУЧАЙ 3: БОЛЬШЕ ОГРАНИЧЕНИЙ
const func = <A extends SubType>(a: A) => `hello!`
// call examples
const c0 = func(undefined) // error
const c1 = func(null) // error
const c2 = func(() => undefined) // error
const c3 = func(10) // error
const c4 = func(`hi`) // error
const c5 = func({}) // error
const c6 = func(foo) // error <-- Restricted now
const c7 = func(foo_SubType) // ok <-- Still allowed
const c8 = func(foo_DiffSubType) // error <-- NO MORE ALLOWED !
См в TS площадка EM>
ЗАКЛЮЧЕНИЕ
Функция ниже:
const func = <A extends Foo>(a: A = foo_SubType) => `hello!` //error!
Выдает это сообщение об ошибке:
Type 'SubType' is not assignable to type 'A'.
'SubType' is assignable to the constraint of type 'A', but 'A'
could be instantiated with a different subtype of constraint
'Foo'.ts(2322)
Поскольку Typescript выводит A
из вызова функции, но в языке нет ограничений, ограничивающих вас на вызов функции с разными подтипами 'Foo'. Например, все приведенные ниже вызовы функции считаются действительными:
const c0 = func(foo) // ok! type 'Foo' will be infered and assigned to 'A'
const c1 = func(foo_SubType) // ok! type 'SubType' will be infered
const c2 = func(foo_DiffSubType) // ok! type 'DiffSubType' will be infered
Следовательно, присвоение конкретного типа общему Type Parameter
неверно, потому что в TS Type Parameter
может всегда быть инстанцирован для некоторого произвольного другого подтипа.
Решение:
Никогда не назначайте конкретный тип параметру универсального типа, считайте его read-only
! Вместо этого сделайте следующее:
const func = <A extends Foo>(a: A) => `hello!` //ok!
См TS площадка
person
Flavio Vilante
schedule
16.12.2019