Как получить ключи интерфейса TypeScript в виде массива строк?
Проблема: ключи интерфейса недоступны в рантайме
Главная сложность в том, что интерфейсы TypeScript существуют только на этапе компиляции. В скомпилированном JavaScript от них не остаётся и следа. Поэтому получить массив ключей напрямую из интерфейса невозможно — нужно использовать объект, реализующий этот интерфейс, либо применять специальные техники.
Решение 1: Через объект с типом (простой способ)
Самый надёжный способ — создать объект, который соответствует интерфейсу, и взять его ключи через Object.keys(). TypeScript позволит это сделать, если правильно указать тип:
interface User {
name: string;
age: number;
email: string;
}
// Создаём объект с явным указанием типа
const user: User = {
name: 'Alice',
age: 30,
email: 'alice@example.com'
};
// Получаем массив ключей
const keys = Object.keys(user) as Array<keyof User>;
// keys: ("name" | "age" | "email")[]
console.log(keys); // ['name', 'age', 'email']
Важно: Используйте as Array<keyof User>, чтобы сохранить тип ключей. Без этого TypeScript вернёт string[].
Решение 2: Функция-утилита с проверкой типов
Если вам нужно получать ключи разных интерфейсов, напишите универсальную функцию:
function getKeys<T extends object>(obj: T): Array<keyof T> {
return Object.keys(obj) as Array<keyof T>;
}
// Использование
const userKeys = getKeys({ name: 'Bob', age: 25 });
// userKeys: ("name" | "age")[]
Решение 3: Через класс (для динамических данных)
Если данные приходят из API и вы не можете создать объект заранее, используйте класс с конструктором:
class UserEntity {
constructor(
public name: string,
public age: number,
public email: string
) {}
}
const user = new UserEntity('Charlie', 35, 'charlie@mail.com');
const keys = Object.keys(user) as Array<keyof UserEntity>;
// keys: ("name" | "age" | "email")[]
Решение 4: Компиляторный хак (только для продвинутых)
Существует недокументированный способ использовать ts-transformer-keys или typescript-json-schema, но это требует настройки сборки. Не рекомендую новичкам.
Что делать, если объекта нет?
Если вы работаете с типами, а не с объектами (например, в generic-функции), можно использовать тип-маппер:
type KeysArray<T> = Array<keyof T>;
// Пример использования
function processUser<T extends User>(data: T) {
const keys: KeysArray<T> = Object.keys(data) as KeysArray<T>;
// Работаем с keys
}
Важное предостережение
Помните: Object.keys() возвращает только собственные (не унаследованные) ключи объекта. Если интерфейс наследуется, метод может не вернуть все ключи родительского типа. Используйте for...in с проверкой hasOwnProperty для полного обхода.
Надеюсь, это поможет! Выбирайте способ в зависимости от вашей задачи. Для большинства случаев достаточно первого варианта с явным созданием объекта.
<<Альтернативный подход: Использование mapped types
Если вам нужно получить ключи интерфейса без создания объекта, можно применить трюк с mapped types и пустым объектом. Этот способ подходит, когда у вас есть только тип, но нет данных:
interface User {
name: string;
age: number;
email: string;
}
// Создаём тип-заглушку
type UserKeys = keyof User;
// UserKeys = "name" | "age" | "email"
// Приводим к массиву через пустой объект
const keys = Object.keys({} as User) as Array<UserKeys>;
// Но это вернёт пустой массив! Не подходит.
Этот метод не сработает, так как Object.keys на пустом объекте вернёт []. Поэтому для реального получения ключей всё равно нужен объект с данными.
Дополнение: Использование декораторов (экспериментально)
Если вы используете экспериментальные декораторы TypeScript, можно создать декоратор, который записывает ключи в статическое поле:
function RecordKeys<T>(target: T) {
const keys: Array<keyof T> = Object.keys(target.prototype) as any;
target.keys = keys;
}
@RecordKeys
class User {
constructor(public name: string, public age: number) {}
}
console.log(User.keys); // ['name', 'age'] (требует настройки)
Но это сложный и нестандартный путь. Лучше придерживаться первого ответа.
Ответов пока нет.
Войдите чтобы оставить ответ