Почему Object.keys не возвращает ключ typeof в TypeScript? - программирование
Подтвердить что ты не робот

Почему Object.keys не возвращает ключ typeof в TypeScript?

Заголовок говорит сам за себя - почему Object.keys(x) в TypeScript не возвращает тип Array<keyof typeof x>? Это то, что делает Object.keys, так что со стороны авторов файла определения TypeScript кажется очевидным упущением не делать возвращаемый тип просто keyof T

Должен ли я регистрировать ошибку в репозитории GitHub или просто отправить PR, чтобы исправить это для них?

4b9b3361

Ответ 1

Текущий тип возвращаемого значения (string[]) является преднамеренным. Зачем?

Рассмотрим такой тип:

interface Point {
    x: number;
    y: number;
}

Вы пишете такой код:

function fn(k: keyof Point) {
    if (k === "x") {
        console.log("X axis");
    } else if (k === "y") {
        console.log("Y axis");
    } else {
        throw new Error("This is impossible");
    }
}

Позвольте задать вопрос:

В хорошо ли типизированной программе может ли законный вызов fn вызвать ошибку?

Желаемый ответ, конечно, "Нет". Но какое это имеет отношение к Object.keys?

Теперь рассмотрим этот другой код:

interface NamedPoint extends Point {
    name: string;
}

const origin: NamedPoint = { name: "origin", x: 0, y: 0 };

Обратите внимание, что в соответствии с системой типа машинописи, все NamedPoint действительны Point s.

Теперь давайте напишем немного больше кода:

function doSomething(pt: Point) {
    for (const k of Object.keys(pt)) {
        // A valid call iff Object.keys(pt) returns (keyof Point)[]
        fn(k);
    }
}
// Throws an exception
doSomething(origin);

Наша хорошо набранная программа просто выбросила исключение!

Здесь что-то пошло не так! Возвращая keyof T из Object.keys, мы нарушили предположение, что keyof T образует исчерпывающий список, потому что наличие ссылки на объект не означает, что тип ссылки не является супертипом типа значение.

По сути, (по крайней мере) одна из следующих четырех вещей не может быть правдой:

  1. keyof T - исчерпывающий список ключей T
  2. Тип с дополнительными свойствами всегда является подтипом своего базового типа.
  3. Допустимо псевдоним значения подтипа ссылкой на супертип
  4. Object.keys возвращает keyof T

Выбрасывание точки 1 делает keyof практически бесполезным, поскольку подразумевает, что keyof Point может быть некоторым значением, keyof Point "x" или "y".

Отбрасывание точки 2 полностью разрушает систему типов TypeScript. Не вариант.

Отбрасывание точки 3 также полностью разрушает систему типов TypeScript.

Отбрасывание пункта 4 - это хорошо, и вы, программист, задумываетесь, является ли объект, с которым вы имеете дело, псевдонимом для подтипа того, что, по вашему мнению, у вас есть.

"Отсутствующей функцией", которая делает это законным, но не противоречивым, являются Точные Типы, которые позволили бы вам объявить новый тип типа, который не подпадает под пункт № 2. Если бы эта функция существовала, вероятно, можно было бы заставить Object.keys возвращать keyof T только для T которые были объявлены как точные.