Что на самом деле означает подпись индекса TypeScript? - PullRequest
8 голосов
/ 19 октября 2019

Я уже некоторое время пишу TypeScript и не понимаю, что означает подпись индекса.

Например, этот код допустим:

function fn(obj: { [x: string]: number }) {
    let n: number = obj.something;
}

Но этот код, которыйделает в основном то же самое, не так:

function fn(obj: { [x: string]: number }) {
    let p: { something: number } = obj;
}

Это ошибка? Что это означает?

1 Ответ

9 голосов
/ 19 октября 2019

Вы правы, что запутались. Подписи индекса означают несколько вещей, и они означают немного разные вещи в зависимости от того, где и как вы спрашиваете.

Во-первых, подписи индекса подразумевают, что все объявленные свойства в типе должны иметь совместимый тип .

interface NotLegal {
    // Error, 'string' isn't assignable to 'number'
    x: string;
    [key: string]: number;
}

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

function fn(obj: NotLegal) {
    // 'n' would have a 'string' value
    const n: number = obj[String.fromCharCode(120)];
}

Во-вторых, подписи индекса позволяют выполнять запись в любой индекс с совместимым типом .

interface NameMap {
    [name: string]: number;
}
function setAge(ageLookup: NameMap, name: string, age: number) {
    ageLookup[name] = age;
}

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

В-третьих, подписи индекса подразумевают существование любого свойства, которое вы конкретно запрашиваете:

interface NameMap {
    [name: string]: number;
}
function getMyAge(ageLookup: NameMap) {
    // Inferred return type is 'number'
    return ageLookup["RyanC"];
}

Поскольку x["p"] и x.p ведут себя одинаково в JavaScript, TypeScript обрабатывает их эквивалентно:

// Equivalent
function getMyAge(ageLookup: NameMap) {
    return ageLookup.RyanC;
}

Это согласуется с тем, как TypeScript просматривает массивы, то есть предполагается, что доступ к массиву является внутренним. Он также эргономичен для подписей индекса, потому что, как правило, у вас есть известный набор ключей, и вам не нужно выполнять дополнительную проверку:

interface NameMap {
    [name: string]: number;
}
function getAges(ageLookup: NameMap) {
    const ages = [];
    for (const k of Object.keys(ageLookup)) {
        ages.push(ageLookup[k]);
    }
    return ages;
}

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

interface Point {
    x: number;
    y: number;
}
interface NameMap {
    [name: string]: number;
}
const m: NameMap = {};
// Not OK, which is good, because p.x is undefined
const p: Point = m;

Этот вид присваиванияочень маловероятно, что это будет правильным на практике!

Это отличительная черта между { [k: string]: any } и any - вы можете читать и записывать свойства любого типа на объекте с сигнатурой индекса, но это может 'не может использоваться вместо любого типа, например any.

Каждое из этих поведений индивидуально очень оправдано, но в целом наблюдаются некоторые несоответствия.

Например,эти две функции идентичны с точки зрения их поведения во время выполнения, но TypeScript считает, что только одна из них была вызвана неправильно:

interface Point {
    x: number;
    y: number;
}
interface NameMap {
    [name: string]: number;
}

function A(x: NameMap) {
    console.log(x.y);
}

function B(x: Point) {
    console.log(x.y);
}
const m: NameMap = { };
A(m); // OK
B(m); // Error

В целом, когда вы пишете сигнатуру индекса для типа, вы говорите:

  • Это нормально для чтения / записи этого объекта с произвольным ключом
  • Когда я читаю определенное свойство из этого объекта через произвольный ключ, оно всегда присутствует
  • Этот объект не имеет буквально каждого имени свойства в целях совместимости типов
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...