web-dev-qa-db-fra.com

Comment éviter l'erreur "La signature d'index du type d'objet comporte implicitement un type" tout type "lors de la compilation de TypeScript avec l'indicateur noImplicitAny activé?

Je compile toujours TypeScript avec le drapeau --noImplicitAny. Cela a du sens, car je veux que ma vérification de type soit la plus précise possible.

Mon problème est qu'avec le code suivant j'obtiens l'erreur 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];

Il est important de noter que l'idée est que la variable clé provient d'un autre endroit de l'application et peut être n'importe laquelle des clés de l'objet.

J'ai essayé explicitement de convertir le type en:

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

Ou est-ce que mon scénario n'est tout simplement pas possible avec --noImplicitAny?

211
Jasper Schulte

L'ajout d'une signature d'index permettra à TypeScript de savoir quel type doit être.

Dans votre cas, ce serait [key: string]: string;

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

Toutefois, cela impose également à tous les types de propriété de correspondre à la signature d'index. Puisque toutes les propriétés sont une string cela fonctionne.

Bien que les signatures d'index soient un moyen puissant pour décrire le tableau et le modèle 'dictionnaire', elles imposent également que toutes les propriétés correspondent à leur type de retour.

Modifier:

Si les types ne correspondent pas, un type d'union peut être utilisé [key: string]: string|IOtherObject;

Avec les types d'union, il est préférable de laisser TypeScript déduire le type au lieu de le définir.

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

Bien que cela puisse devenir un peu brouillon si vous avez beaucoup de types différents dans les signatures d'index. L’autre solution consiste à utiliser any dans la signature. [key: string]: any; Ensuite, vous aurez besoin de lancer les types comme vous l'avez fait ci-dessus.

211
thoughtrepo

Une autre façon d'éviter l'erreur consiste à utiliser le casting comme ceci:

let secondValue: string = (<any>someObject)[key]; (notez la parenthèse)

Le seul problème est que cela n’est plus du type sûr, car vous appelez any. Mais vous pouvez toujours utiliser le type correct.

ps: J'utilise TypeScript 1.7, pas sûr des versions précédentes.

156
Pedro Villa Verde

TypeScript 2.1 a présenté une manière élégante de traiter ce problème.

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

Nous pouvons accéder à tous les noms de propriété d’objet pendant la phase de compilation avec le mot clé keyof (voir changelog ).

Il vous suffit de remplacer le type de variable string par keyof ISomeObject. Le compilateur sait maintenant que la variable key est autorisée à contenir uniquement les noms de propriété issus de ISomeObject.

Exemple complet:

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

Code Live sur typescriptlang.org (option noImplicitAny)

Pour en savoir plus avec plus keyof usages .

56
Piotr Lewandowski

Le paramètre tsconfig suivant vous permettra d'ignorer ces erreurs - définissez-le sur true.

suppressImplicitAnyIndexErrors

Supprimez les erreurs noImplicitAny pour les objets d'indexation dépourvus de signatures d'index.

48
Scott Munro

La solution "clé de" mentionnée ci-dessus fonctionne. Mais si la variable n’est utilisée qu’une seule fois, par exemple en parcourant un objet, etc., vous pouvez également la transtyper.

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

Semblable à la réponse de @Piotr Lewandowski, mais dans une forEach:

const config: MyConfig = { ... };

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

Déclarez l'objet comme ceci.

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

Créer une interface pour définir l'interface 'indexeur'

Créez ensuite votre objet avec cet index. 

Remarque: cela aura toujours les mêmes problèmes que ceux décrits dans d'autres réponses concernant l'application du type de chaque élément - mais c'est souvent exactement ce que vous voulez. 

Vous pouvez définir le paramètre de type générique selon vos besoins: 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];

TypeScript Playground


Vous pouvez même l'utiliser dans un contrainte générique lors de la définition d'un type générique: 

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

Alors T dans la classe peut être indexé :-)

2
Simon_Weaver

Pas d'indexeur? Alors faites le vôtre!

J'ai globalement défini cela comme un moyen facile de définir une signature d'objet. T peut être any si nécessaire:

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

Je viens d'ajouter indexer en tant que membre de la classe.

indexer = this as unknown as Indexer<Fruit>;

Alors je me retrouve avec ça:

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

L'avantage de cette opération est que vous récupérez le type approprié: de nombreuses solutions utilisant <any> perdront la saisie à votre place. Rappelez-vous que ceci n'effectue aucune vérification à l'exécution. Vous devrez toujours vérifier si quelque chose existe si vous ne savez pas avec certitude qu'il existe.

Si vous voulez être trop prudent et que vous utilisez strict, vous pouvez le faire pour révéler tous les endroits où vous devrez peut-être effectuer une vérification explicite non définie:

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

Je ne trouve généralement pas cela nécessaire car si j'ai comme propriété de chaîne quelque part, je sais habituellement que c'est valide.

J'ai trouvé cette méthode particulièrement utile si beaucoup de code doit accéder à l'indexeur et que la saisie peut être modifiée à un seul endroit.

Remarque: J'utilise le mode strict, et la unknown est absolument nécessaire. 

Le code compilé sera simplement indexer = this, il est donc très similaire à lorsque TypeScript crée _this = this pour vous.

1
Simon_Weaver

La solution la plus simple que j'ai pu utiliser avec TypeScript 3.1 en 3 étapes est:

1) faire l'interface

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

2) Faites une copie dactylographiée

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

3) Utilisez n'importe où (JSX inclus)

<input customProp={copy} />
0
Artokun

Aujourd’hui, la meilleure solution consiste à déclarer les types. Comme 

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];
0
Artsiom Tymchanka

utiliser keyof typeof

const cat = {
    name: 'tuntun'
}

const key: string = 'name' 

cat[key as keyof typeof cat]
0
alsotang