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
?
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.
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.
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 .
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.
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];
}
Semblable à la réponse de @Piotr Lewandowski, mais dans une forEach
:
const config: MyConfig = { ... };
Object.keys(config)
.forEach((key: keyof MyConfig) => {
if (config[key]) {
// ...
}
});
Déclarez l'objet comme ceci.
export interface Thread {
id:number;
messageIds: number[];
participants: {
[key:number]: number
};
}
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];
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é :-)
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.
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} />
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];
utiliser keyof typeof
const cat = {
name: 'tuntun'
}
const key: string = 'name'
cat[key as keyof typeof cat]