typescript - tutorial - Como evito o erro "A assinatura do índice do tipo de objeto tem implicitamente um tipo 'qualquer'" ao compilar o texto datilografado com o sinalizador noImplicitAny ativado?




typescript vs javascript (9)

Eu sempre compilei o Typecript com a flag --noImplicitAny. Isso faz sentido, pois quero que minha verificação de tipo seja o mais rígida possível.

Meu problema é que, com o código a seguir, recebo o erro A 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];

Importante notar é que a ideia é que a variável chave venha de algum outro lugar do aplicativo e possa ser qualquer uma das chaves do objeto.

Eu tentei converter explicitamente o tipo por:

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

Ou o meu cenário não é possível com --noImplicitAny ?


Crie uma interface para definir a interface 'indexador'

Em seguida, crie seu objeto com esse índice.

Nota: isso ainda terá os mesmos problemas que outras respostas descreveram em relação à imposição do tipo de cada item - mas geralmente é exatamente isso que você deseja.

Você pode criar o parâmetro de tipo genérico do que precisar: 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];

Parque de diversões

Você pode até usar isso em uma restrição genérica ao definir um tipo genérico:

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

Então T dentro da classe pode ser indexado :-)


Nenhum indexador? Então faça o seu!

Eu defini isso globalmente como uma maneira fácil de definir uma assinatura de objeto. T pode ser any se necessário:

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

Acabei de adicionar o indexer como um membro da classe.

indexer = this as unknown as Indexer<Fruit>;

Então, acabo com isso:

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

O benefício de fazer isso é obter o tipo adequado de volta - muitas soluções que usam <any> perderão a digitação para você. Lembre-se de que isso não realiza nenhuma verificação de tempo de execução. Você ainda precisará verificar se existe algo, se não tiver certeza.

Se você quer ser excessivamente cauteloso e usa strict pode fazer isso para revelar todos os lugares em que pode ser necessário realizar uma verificação explícita e indefinida:

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

Normalmente, não acho isso necessário, pois se eu tiver uma propriedade string de algum lugar, geralmente sei que é válida.

Achei esse método especialmente útil se tiver muito código que precisa acessar o indexador e a digitação pode ser alterada em apenas um local.

Nota: estou usando strict modo strict e o unknown é definitivamente necessário.

O código compilado será apenas indexer = this , portanto, é muito semelhante a quando o _this = this datilografado cria _this = this para você.


A seguinte configuração do tsconfig permitirá que você ignore esses erros - defina-o como true.

suppressImplicitAnyIndexErrors

Suprimir erros noImplicitAny para objetos de indexação sem assinaturas de índice.


A solução 'keyof' mencionada acima funciona. Mas se a variável for usada apenas uma vez, por exemplo, percorrendo um objeto, etc, você também poderá convertê-lo.

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

Atualmente, a melhor solução é declarar tipos. Gostar

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

Declare o objeto assim.

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

Outra maneira de evitar o erro é usar o elenco assim:

let secondValue: string = (<any>someObject)[key]; (Observe os parênteses)

O único problema é que isso não é mais seguro para o tipo, como você está transmitindo para any . Mas você sempre pode voltar ao tipo correto.

ps: Estou usando o texto datilografado 1.7, não tenho certeza das versões anteriores.


Semelhante à resposta de @Piotr Lewandowski, mas dentro de um forEach :

const config: MyConfig = { ... };

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

O TypeScript 2.1 introduziu uma maneira elegante de lidar com esse problema.

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

Podemos acessar todos os nomes de propriedades de objetos durante a fase de compilação por keyof keyword (consulte changelog ).

Você só precisa substituir o tipo de variável de string por keyof ISomeObject . Agora, o compilador sabe que a variável key pode conter apenas nomes de propriedades do ISomeObject .

Exemplo completo:

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

Código noImplicitAny no typescriptlang.org (opção set noImplicitAny )

Leitura adicional com mais keyof usos .