общий ключ ограничения typescript для T и строки: ts2322

#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 должен быть keyof T , поэтому 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
  }
 

Теперь все компилируется без ошибок.

Ссылка на игровую площадку для кода