#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