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


309

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 seguinte código, recebo o erro 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 no aplicativo e possa ser qualquer uma das chaves no objeto.

Eu tentei converter explicitamente o tipo por:

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

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

Respostas:


337

Adicionar uma assinatura de índice permitirá que o TypeScript saiba qual deve ser o tipo.

No seu caso, isso seria [key: string]: string;

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

No entanto, isso também impõe todos os tipos de propriedades para corresponder à assinatura do índice. Uma vez que todas as propriedades são um, stringele funciona.

Embora as assinaturas de índice sejam uma maneira poderosa de descrever a matriz e o padrão de 'dicionário', elas também impõem que todas as propriedades correspondam ao tipo de retorno.

Editar:

Se os tipos não corresponderem, um tipo de união pode ser usado [key: string]: string|IOtherObject;

Com os tipos de união, é melhor deixar o TypeScript inferir o tipo em vez de defini-lo.

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

Embora isso possa ficar um pouco confuso se você tiver muitos tipos diferentes nas assinaturas de índice. A alternativa é usar anya assinatura. [key: string]: any;Então você precisaria converter os tipos como você fez acima.


E se sua interface se parece com a interface ISomeObject {firstKey: string; secondKey: IOtherObject; } isso não é possível, eu acho?
Jasper Schulte

Obrigado! A combinação de um tipo qualquer com a conversão de um tipo por caso parece um caminho a percorrer.
Jasper Schulte

Oi, Como lidar com o "anyObject [key: Object] ['name']"?
Code_Crash 16/09/19

ou diga algo como _obj = {}; deixe _dbKey = _props [chave] ['nome']; _obj [_dbKey] = esta [chave]; aqui _props is object e object [key] também retornará um objeto que terá a propriedade name.
Code_Crash

180

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á usando any. Mas você sempre pode voltar ao tipo correto.

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


20
Para evitar avisos tslint, você também pode usar:let secondValue: string = (someObject as any)[key];
briosheje

97

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 keyofpalavra-chave (consulte changelog ).

Você só precisa substituir o stringtipo de variável por keyof ISomeObject. Agora, o compilador sabe que a keyvariável pode conter apenas nomes de propriedades 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 ativo em typescriptlang.org ( noImplicitAnyopção definida )

Leitura adicional com mais keyofusos .


6
No entanto, não funcionará se declararmos keycomo const key = (keyof ISomeObject)= 'segundo' + 'Chave'
Decepcionado

55

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.


14
isso é algo que você não deve fazer - provavelmente alguém da sua equipe definiu explicitamente essa opção do compilador para tornar o código mais à prova de balas!
Atsu85

12
Discordo que é exatamente para isso que esta opção foi feita: Permitir a notação de colchetes --noImplicitAny. Combine perfeitamente a pergunta do op.
Ghetolay

4
Eu concordo com @Ghetolay. Essa também é a única opção se a modificação da interface não for possível. Por exemplo, com interfaces internas como XMLHttpRequest.
Marco Roy

1
Também concordo com @Ghetolay. Estou curioso para saber como isso é qualitativamente diferente da resposta de Pedro Villa Verde (além do fato de o código ser menos feio). Todos sabemos que o acesso a uma propriedade de objeto usando uma string deve ser evitado, se possível, mas às vezes desfrutamos dessa liberdade enquanto compreendemos os riscos.
Stephen Paul

São apenas trocas. Escolha o que você gosta: menos área de superfície de erro e acesso estrito ao índice, ou tenha mais área de superfície para erros e acesse facilmente índices desconhecidos. O keyofoperador TS2.1 pode ajudar a manter tudo rígido, veja a resposta da Piotr!
trusktr

24

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

Obrigado. Isso funciona para acesso arbitrário à chave ao iterar as chaves de outro objeto.
bucabay 12/03/19

19

usar keyof typeof

const cat = {
    name: 'tuntun'
}

const key: string = 'name' 

cat[key as keyof typeof cat]

7

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

const config: MyConfig = { ... };

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

Como você conseguiu que isso funcionasse? Eu estou tentando a mesma coisa (TS 3.8.3), embora ele lança um erro dizendo: Argument of type '(field: "id" | "url" | "name") => void' is not assignable to parameter of type '(value: string, index: number, array: string[]) => void'. Meu código é assim Object.keys(components).forEach((comp: Component) => {...}, onde Componenté um tipo (como MyConfig).
theGirrafish

6

Declare o objeto assim.

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

6

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

Eu defini isso globalmente como uma maneira fácil de definir uma assinatura de objeto. Tpode ser, anyse necessário:

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

Acabei de adicionar indexercomo 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 é que você recebe o tipo adequado de volta - muitas soluções usadas <any>perdem 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ê deseja ser excessivamente cauteloso, e está usando, strictvocê pode fazer isso para revelar todos os lugares em que pode ser necessário fazer uma verificação indefinida explícita:

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 o strictmodo, e o unknowné definitivamente necessário.

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


1
Em alguns casos, você pode usar o Record<T>tipo - no momento, não sou capaz de pesquisar os detalhes, mas, em alguns casos limitados, pode funcionar melhor.
Simon_Weaver

5

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>

// 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 Tdentro da classe pode ser indexado :-)


Eu não acho que exista uma interface 'embutida' padrão para Dictionaryisso { [key: string]: T }, mas se houver, edite esta pergunta para remover minha ObjectIndexer.
Simon_Weaver 23/01/19

3

Declare o tipo em que sua chave é string e o valor pode ser qualquer um e, em seguida, declare o objeto com esse tipo e o fiapo não será exibido

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

Portanto, seu código será

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

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

    let key: string = 'secondKey';

    let secondValue: string = someObject[key];

1

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

1

A solução mais simples que eu encontrei usando o Typescript 3.1 em 3 etapas é:

1) Faça interface

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

2) Faça uma cópia digitada

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

3) Use em qualquer lugar (JSX incluído)

<input customProp={copy} />
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.