Как предотвратить ошибку «Сигнатура индекса типа объекта неявно имеет тип« любой »» при компиляции машинописи с включенным флагом noImplicitAny?

190

Я всегда компилирую 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?

Теги:

11 ответов

173
Лучший ответ

Добавление индексной подписи позволит 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; Затем вам нужно будет использовать типы, подобные тому, что вы делали выше.

  • 0
    И если ваш интерфейс выглядит как интерфейс ISomeObject {firstKey: string; secondKey: IOtherObject; } это не возможно, я думаю?
  • 0
    Нет, есть способ, обновил ответ.
Показать ещё 1 комментарий
144

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

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

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

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

  • 13
    Чтобы избежать предупреждений tslint, вы также можете использовать: let secondValue: string = (someObject as any)[key];
47

В 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
    Однако это не будет работать, если мы объявим key как const key = (keyof ISomeObject) = 'second' + 'Key'
46

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

suppressImplicitAnyIndexErrors

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

  • 13
    Это то, что вы не должны делать - возможно, кто-то в вашей команде явно установил эту опцию компилятора, чтобы сделать код более пуленепробиваемым!
  • 11
    Я не согласен, это именно то, для чего была сделана эта опция: Разрешить нотацию в скобках с --noImplicitAny . Совпадение идеально оп вопрос.
Показать ещё 3 комментария
8

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

for (const key in someObject) {
    sampleObject[key] = someObject[key as keyof ISomeObject];
}
  • 0
    Благодарю. Это работает для произвольного доступа к ключу при итерации ключей другого объекта.
6

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

const config: MyConfig = { ... };

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

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

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

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

Я глобально определил это как простой способ определить сигнатуру объекта. 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 для вас.

0

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

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

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

Вы можете сделать универсальный параметр типа любым, что вам нужно: 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 внутри класса может быть проиндексирован :-)

  • 0
    Я не думаю, что есть стандартный «встроенный» интерфейс для Dictionary который представляет { [key: string]: T } , но если есть, отредактируйте этот вопрос, чтобы удалить мой ObjectIndexer .
0

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

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

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

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

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

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

<input customProp={copy} />
0

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

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];

Ещё вопросы

Сообщество Overcoder
Наверх
Меню