Ключ для вывода строки | числа, когда ключ является только строкой

#typescript

#typescript #typescript-типизации

Вопрос:

Я определяю AbstractModel примерно так:

 export interface AbstractModel {
   [key: string]: any
}
  

Затем я объявляю тип Keys :

 export type Keys = keyof AbstractModel;
  

Я ожидал бы, что все, что имеет тип ключей, будет однозначно интерпретироваться как строка, например:

 const test: Keys;
test.toLowercase(); // Error: Property 'toLowerCase' does not exist on type 'string | number'. Property 'toLowerCase' does not exist on type 'number'.
  

Это ошибка Typescript (2.9.2), или я что-то упускаю?

Ответ №1:

Как определено в примечаниях к выпуску TypeScript 2.9, если вы вводите ключ интерфейса со строковой индексной подписью, он возвращает объединение строки и числа

Учитывая тип объекта X, ключ X решается следующим образом:

Если X содержит подпись индекса строки, ключ X представляет собой объединение типов string, number и literal, представляющих символьные свойства, в противном случае

Если X содержит числовую индексную подпись, ключ X представляет собой объединение числовых и литеральных типов, представляющих строковые и символьные свойства, в противном случае

ключ X — это объединение литеральных типов, представляющих строковые, числовые и символьные свойства.

Источник

Это потому, что: JavaScript преобразует числа в строки при индексации объекта:

[..] при индексации с помощью числа JavaScript фактически преобразует его в строку перед индексацией в объект. Это означает, что индексирование с помощью 100 (число) — это то же самое, что и индексирование с помощью «100» (строка), поэтому они должны быть согласованы.

Источник

Пример:

 let abc: AbstractModel = {
    1: "one",
};

console.log(abc[1] === abc["1"]); // true
  

Если вам нужны только строковые ключи, тогда вы можете извлекать только строковые ключи из своего интерфейса следующим образом:

 type StringKeys = Extract<keyof AbstractModel, string>;

const test: StringKeys;
test.toLowerCase(); // no error
  

Также компилятор TypeScript предоставляет возможность получить поведение до 2.9 keyof :

keyofStringsOnly (логическое значение) по умолчанию false

Разрешите keyof использовать только строковые имена свойств (без цифр или символов).

Источник

Комментарии:

1. Extract<keyof Record<string, any>, "string"> всегда возвращайте never вместо string … почему ?

2. @Aure77 Попробуйте убрать двойные кавычки вокруг string

Ответ №2:

Я столкнулся с подобной проблемой. Я решил это, принудив key быть string:

 export type Keys = keyof AbstractModel amp; string;
  

Другим вариантом может быть преобразование ключа в строку: test.toString().toLowercase()

Комментарии:

1. Очень полезная, отличная контекстная альтернатива keyofStringsOnly: true глобальной настройке параметра.

2. Хм … так почему это не работает type PickNumericProperties<T> = Pick<T, keyof {[k: keyof T]: number}> ?

3. Есть ли какие-либо практические различия между этим и Extract<keyof AbstractModel, string> ??

Ответ №3:

Для универсальной утилиты typescript вы можете использовать следующее:

 type KeyOf<T extends object> = Extract<keyof T, string>;
  

Использование:

 const sym = Symbol();

const obj = {
  [sym]: true,
  foo: 'foobar',
  bar: 'barfoo',
  1: 'lorem'
}

let key: KeyOf<typeof obj> = 'foo'; // 'foo' | 'bar'

key = 'bar'; // ok
key = 'fool'; // error
key = 1; // error
  

игровая площадка