#typescript #typescript-generics
#typescript #typescript-generics
Вопрос:
Я пытаюсь написать универсальный класс, который хранит пару специальных указателей на ключи универсального типа. Вот пример игровой площадки этого MVP
const _idKey = Symbol('_idKey')
const _sortKey = Symbol('_sortKey')
export interface BaseStoreConfig<T, Tid extends keyof T, Tsk extends keyof T | undefined> {
idKey?: Tid
sortKey?: Tsk
}
export class BaseStore<T, Tid extends keyof T amp; string, Tsk extends keyof T | undefined> {
public [_idKey]: keyof T | 'id'
public [_sortKey]?: keyof T | undefined
constructor({
idKey = 'id', // Errors, see below
sortKey,
}: BaseStoreConfig<T, Tid, Tsk>) {
this[_idKey] = idKey
this[_sortKey] = sortKey
}
}
Это приводит к ошибке ts2322 (я пробовал несколько вариантов Tid
ограничений, я всегда возвращаюсь к этой ошибке)
Type 'string' is not assignable to type 'Tid'.
'string' is assignable to the constraint of type 'Tid', but 'Tid'
could be instantiated with a different subtype of constraint 'string'.ts(2322)
Обычно я понимаю эту ошибку, но в данном случае я в замешательстве. Как подтип string
не может быть присвоен этому типу? есть ли какой-либо способ выразить это ограничение?
Комментарии:
1. Подумайте
const z: 'foo' amp; string = 'id';
— это не сработает, потому что ‘id’ нельзя присвоить ‘foo’, поэтому здесь у вас возникает та же проблема, если вы передаете некоторый T с ключами, отличными от ‘id’. Что касается «есть ли какой-либо способ выразить это ограничение?» — не уверен на 100% в том, что вы пытаетесь здесь сделать. Я думаю, что ваша проектная точка использования «id» в качестве ключа по умолчанию, без каких-либо предположений о форме буквы T, приведет к путанице при вводе текста. Возможно, вы могли бы добавить несколько примеров использования, что должно работать, что не должно и т. Д., И я мог бы помочь в дальнейшем2. Я не совсем понимаю это.
Tid
должен быть keyofT
, поэтомуT
не может быть'foo'
, иначе не было бы никакого возможного типа дляTid
. Разве использованиеkeyof
принужденияT
не должно быть объектом?
Ответ №1:
Я думаю, проблема в том, что Typescript на самом деле не поддерживает разные типы для одного и того же значения (например, idKey
) в зависимости от того, просматривается ли значение со стороны вызывающего ( Tid
или undefined
) или со стороны разработчика ( Tid
или "id"
). Существуют похожие проблемы, такие как microsoft / TypeScript #42053, поданные как ошибки, но я не уверен, когда они будут устранены.
Вы аннотировали параметр конструктора как имеющий тип BaseStoreConfig<T, Tid, Tsk>
, idKey
свойство которого имеет тип Tid | undefined
. При попытке присвоить ему значение "id"
по умолчанию, компилятор видит это как несоответствие… потому "id"
что не может быть присвоен Tid
. Похоже, что конкретная ошибка, упоминаемая string
вместо конкретного "id"
, связана с некоторыми изменениями в TypeScript после версии 3.9 (не уверен, почему, но я предполагаю, что она делает разумные вещи в другом месте). Если вы вернетесь к версии 3.9 и посмотрите на нее, вы увидите "id"
явное упоминание ошибки.
Поэтому я думаю, что исправление здесь заключается в том, чтобы не использовать значения по умолчанию внутри деструктурирования, потому что нет отличного способа представить два разных типа для одного и того же значения. Вместо этого давайте просто переместим значение по умолчанию в тело конструктора:
constructor({
idKey, sortKey,
}: BaseStoreConfig<T, Tid, Tsk>) {
this[_idKey] = idKey ?? "id" // okay
this[_sortKey] = sortKey
}
Теперь все компилируется без ошибок.