Как предотвратить ошибку? Подпись индекса типа объекта неявно имеет тип "any" при компиляции typescript с включенным флагом noImplicitAny?

Я всегда компилирую Typescript с флагом --noImplicitAny. Это имеет смысл, поскольку я хочу, чтобы мой тип проверки был как можно более плотным.

Моя проблема в том, что со следующим кодом я получаю сообщение об ошибке Index signature of object type implicitly has an 'any' type:

interface ISomeObject {
    firstKey:   string;
    secondKey:  string;
    thirdKey:   string;
}

let someObject: ISomeObject = {
    firstKey:   'firstValue',
    secondKey:  'secondValue',
    thirdKey:   'thirdValue'
};

let key: string = 'secondKey';

let secondValue: string = someObject[key];

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

Я пробовал явно лить тип:

let secondValue: string = <string>someObject[key];

Или мой сценарий невозможен с помощью --noImplicitAny?

Ответ 1

Добавление индексной подписи позволит TypeScript знать, какой тип должен быть.

В вашем случае это будет [key: string]: string;

interface ISomeObject {
    firstKey:      string;
    secondKey:     string;
    thirdKey:      string;
    [key: string]: string;
}

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

Хотя индексные подписи являются мощным способом описания массива и шаблона словаря, они также обеспечивают, чтобы все свойства соответствовали их возвращаемому типу.

Edit:

Если типы не совпадают, можно использовать тип объединения [key: string]: string|IOtherObject;

С типами соединений лучше, если вы позволяете TypeScript выводить тип вместо его определения.

// Type of `secondValue` is `string|IOtherObject`
let secondValue = someObject[key];
// Type of `foo` is `string`
let foo = secondValue + '';

Хотя это может немного запутаться, если в индексах есть много разных типов. Альтернативой этому является использование any в сигнатуре. [key: string]: any; Затем вам нужно будет использовать типы, подобные тому, что вы делали выше.

Ответ 2

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

let secondValue: string = (<any>someObject)[key]; (Обратите внимание на скобки)

Единственная проблема заключается в том, что это больше не безопасно для типов, поскольку вы выполняете any. Но вы всегда можете вернуться к правильному типу.

ps: Я использую typescript 1.7, не уверен в предыдущих версиях.

Ответ 3

В TypeScript 2.1 представлен элегантный способ решения этой проблемы.

const key: (keyof ISomeObject) = 'secondKey';
const secondValue: string = someObject[key];

Мы можем получить доступ ко всем именам объектов объекта на этапе keyof ключевыми словами keyof (см. keyof изменений).

Вам нужно только заменить string тип переменной keyof ISomeObject. Теперь компилятор знает, что key переменная разрешена содержать только имена свойств из ISomeObject.

Полный пример:

interface ISomeObject {
    firstKey:   string;
    secondKey:  string;
    thirdKey:   number;
}

const someObject: ISomeObject = {
    firstKey:   'firstValue',
    secondKey:  'secondValue',
    thirdKey:   3
};

const key: (keyof ISomeObject) = 'secondKey';
const secondValue: string = someObject[key];

// You can mix types in interface, keyof will know which types you refer to.
const keyNumber: (keyof ISomeObject) = 'thirdKey';
const numberValue: number = someObject[keyNumber];

Живой код на typescriptlang.org (установите параметр noImplicitAny)

Дальнейшее чтение с использованием большего количества keyof.

Ответ 4

Следующая настройка tsconfig позволит вам игнорировать эти ошибки - установите для нее значение true.

suppressImplicitAnyIndexErrors

Подавить noImplicitAny ошибки для индексирования объектов, не имеющих индексных подписей.

Ответ 5

Вышеупомянутое решение "keyof" работает. Но если переменная используется только один раз, например, через объект и т.д., Вы также можете его выводить.

for (const key in someObject) {
    sampleObject[key] = someObject[key as keyof ISomeObject];
}

Ответ 6

используйте keyof typeof

const cat = {
    name: 'tuntun'
}

const key: string = 'name' 

cat[key as keyof typeof cat]

Ответ 7

Как и ответ @Piotr Lewandowski, но в пределах forEach:

const config: MyConfig = { ... };

Object.keys(config)
  .forEach((key: keyof MyConfig) => {
    if (config[key]) {
      // ...
    }
  });

Ответ 8

Объявите объект следующим образом.

export interface Thread {
    id:number;
    messageIds: number[];
    participants: {
        [key:number]: number
    };
}

Ответ 9

Нет индексатора? Тогда сделай свой собственный!

Я глобально определил это как простой способ определить сигнатуру объекта. T может быть any если необходимо:

type Indexer<T> = { [ key: string ]: T };

Я просто добавляю indexer как член класса.

indexer = this as unknown as Indexer<Fruit>;

Итак, я в конечном итоге с этим:

constructor(private breakpointResponsiveService: FeatureBoxBreakpointResponsiveService) {

}

apple: Fruit<string>;
pear: Fruit<string>;

// just a reference to 'this' at runtime
indexer = this as unknown as Indexer<Fruit>;

something() {

    this.indexer['apple'] = ...    // typed as Fruit

Преимущество этого заключается в том, что вы получите правильный тип - многие решения, использующие <any>, потеряют типизацию для вас. Помните, что это не выполняет никакой проверки во время выполнения. Вам все равно нужно будет проверить, существует ли что-то, если вы точно не знаете, существует ли оно.

Если вы хотите быть чрезмерно осторожным и используете strict режим, вы можете сделать это, чтобы выявить все места, где вам может потребоваться выполнить явную неопределенную проверку:

type OptionalIndexed<T> = { [ key: string ]: T | undefined };

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

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

Примечание: я использую strict режим, и unknown, безусловно, необходимо.

Скомпилированный код будет просто indexer = this, поэтому он очень похож на то, когда _this = this создает _this = this для вас.

Ответ 10

Создайте интерфейс для определения интерфейса индексатора

Затем создайте свой объект с этим индексом.

Примечание: это все равно будет иметь те же проблемы, что и другие ответы, описанные применительно к принудительному типу каждого элемента, но часто это именно то, что вы хотите.

Вы можете сделать универсальный параметр типа любым, что вам нужно: ObjectIndexer< Dog | Cat> ObjectIndexer< Dog | Cat>

// this should be global somewhere, or you may already be 
// using a library that provides such a type
export interface ObjectIndexer<T> {
  [id: string]: T;
}

interface ISomeObject extends ObjectIndexer<string>
{
    firstKey:   string;
    secondKey:  string;
    thirdKey:   string;
}

let someObject: ISomeObject = {
    firstKey:   'firstValue',
    secondKey:  'secondValue',
    thirdKey:   'thirdValue'
};

let key: string = 'secondKey';

let secondValue: string = someObject[key];

{ [id: string]: T; } interface ISomeObject extends ObjectIndexer { firstKey: string; secondKey: string; thirdKey: string; } let someObject: ISomeObject = { firstKey: 'firstValue', secondKey: 'secondValue', thirdKey: 'thirdValue' }; let key: string = 'secondKey'; let secondValue: string = someObject[key]; rel="nofollow noreferrer">Машинопись детская площадка


Вы даже можете использовать это в универсальном ограничении при определении универсального типа:

export class SmartFormGroup<T extends IndexableObject<any>> extends FormGroup

Тогда T внутри класса может быть проиндексирован :-)

Ответ 11

Простейшим решением, которое я мог бы найти с помощью TypScript 3.1 в 3 этапа, является:

1) Сделать интерфейс

interface IOriginal {
    original: { [key: string]: any }
}

2) Сделайте типизированную копию

let copy: IOriginal = (original as any)[key];

3) Используйте везде (включая JSX)

<input customProp={copy} />

Ответ 12

В настоящее время лучшим решением является объявление типов. подобно

enum SomeObjectKeys {
    firstKey = 'firstKey',
    secondKey = 'secondKey',
    thirdKey = 'thirdKey',
}

let someObject: Record<SomeObjectKeys, string> = {
    firstKey:   'firstValue',
    secondKey:  'secondValue',
    thirdKey:   'thirdValue',
};

let key: SomeObjectKeys = 'secondKey';

let secondValue: string = someObject[key];

Ответ 13

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

type MyType = {[key: string]: any};

Итак, ваш код будет

type ISomeType = {[key: string]: any};

    let someObject: ISomeType = {
        firstKey:   'firstValue',
        secondKey:  'secondValue',
        thirdKey:   'thirdValue'
    };

    let key: string = 'secondKey';

    let secondValue: string = someObject[key];