Почему Object.keys не возвращает ключ typeof в TypeScript? - PullRequest
26 голосов
/ 06 марта 2019

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

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

1 Ответ

29 голосов
/ 06 марта 2019

Текущий тип возврата (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 };

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

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

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 образует исчерпывающий список, потому что наличие ссылки на объект не означает, что тип ссылки не является t супертип типа значения .

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

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

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

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

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...