Почему только TypeScript может индексировать объект по строке - PullRequest
3 голосов
/ 28 января 2020

Почему TypeScript может индексировать типизированный объект по строке, если эта строка является константой или простой строковой переменной, но он не может индексировать типизированный объект по строке, если эта строка извлекается из массива

То есть рассмотрим следующий код

class Foo {
    public bar: string = 'hello';

    public test() {
        // this works
        console.log(this['bar'])

        // this also works
        const index = 'bar';
        console.log(this[index])

        // in both cases above I have successfully used
        // a string as an index for my type Foo

        // However, this doesn't work
        const props:string[] = ['bar']
        for(const [key,value] of props.entries()) {
            console.log(value); // prints 'bar' to terminal/console
            console.log(this[value])
        }

        // Nor does this
        for(let i=0;i<props.length;i++) {
            console.log(this[props[i]])
        }

        // when looping over an array of string and trying to use the
        // string to index the object, I get the following error
        // why.ts:20:25 - error TS7053: Element implicitly has an 'any'
        // type because expression of type 'string' can't be used to
        // index type 'Foo'.
    }
}

const foo = new Foo;
foo.test()


class Foo {
    public bar: string = 'hello';

    public test() {
        // this works
        console.log(this['bar'])

        // this also works
        const index = 'bar';
        console.log(this[index])

        // in both cases above I have successfully used
        // a string as an index for my type Foo

        // However, this doesn't work
        const props:string[] = ['bar']
        for(const [key,value] of props.entries()) {
            console.log(value); // prints 'bar' to terminal/console
            console.log(this[value])
        }

        // Nor does this
        for(let i=0;i<props.length;i++) {
            console.log(this[props[i]])
        }

        // when looping over an array of string and trying to use the
        // string to index the object, I get the following error
        // why.ts:20:25 - error TS7053: Element implicitly has an 'any'
        // type because expression of type 'string' can't be used to
        // index type 'Foo'.
    }
}

const foo = new Foo;
foo.test()

Обе эти работы.

console.log(this['bar'])
//...
const index = 'bar';
console.log(this[index])    

TypeScript способен индексировать мой объект по строке.

Однако, последующие примеры, где я зацикливаюсь на массиве строк

const props:string[] = ['bar']
for(const [key,value] of props.entries()) {
    console.log(value); // prints 'bar' to terminal/console
    console.log(this[value])
}

for(let i=0;i<props.length;i++) {
    console.log(this[props[i]])
}            

, не будут запускаться / компилироваться. Я получаю следующую ошибку.

why.ts:42:17 - error TS7053: Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'Foo'.
  No index signature with a parameter of type 'string' was found on type 'Foo'.

42     console.log(foo[value])

Так что это сообщение об ошибке - выражение типа 'string' не может быть использовано для индексации типа 'Foo' , кажется, противоречит моим первым двум примерам.

Так что здесь происходит? Помогите бедному программисту на языке динамического c понять, что TypeScript пытается мне сказать. Бонусные баллы за образец, который фактически позволяет мне перебирать массив строк и использовать один в качестве индекса объекта.

1 Ответ

5 голосов
/ 28 января 2020

Ответ прост: если машинопись может доказать, что доступ безопасен, индексация разрешена.

Когда вы пишете this['bar'] машинописный текст видит строковый литерал, и он может легко проверить, что this имеет свойство bar

Когда вы пишете const index = 'bar';, вы можете подумать, что тип index равно string, но на самом деле это не так, тип index является строковым литеральным типом 'bar', поэтому для машинописного текста будет известно единственно возможное значение в index: 'bar' , Поскольку index может удерживать только строку, машинопись может проверить, что доступ this[index] действителен, проверив, что this имеет свойство bar

Когда вы пишете const props:string[] машинопись не сделает никаких других выводов около props это массив string. Это означает, что при доступе к this[prop] машинопись должна быть уверена, что this индексируется любым string, что, поскольку у него нет сигнатуры индекса, это не так, и, следовательно, при доступе возникает ошибка. Если вы используете as const, чтобы сделать вывод логических типов ts для массива вместо string и удалить явную аннотацию, вы сможете выполнить индексный доступ:

const props = ['bar'] as const
for(const [key,value] of props.entries()) {
    console.log(value); 
    console.log(this[value])//ok
}

for(let i=0;i<props.length;i++) {
    console.log(this[props[i]])
}

Playground Link

Вы также можете использовать утверждение типа, если уверены, что prop является ключом this:

const props = ['bar']
for(const [key,value] of props.entries()) {
    console.log(this[value as keyof this])
}

Или, если вы хотите по-настоящему вычурно, вы можете используйте защиту от пользовательских типов или утверждение пользовательских типов, но здесь это кажется излишним.

...