80
votes

Typographie: Aucune signature d'index avec un paramètre de type 'string' n'a été trouvée sur le type '{"A": string; }

J'ai du code javascript vanille qui prend une entrée de chaîne, divise la chaîne en caractères, puis associe ces caractères à une clé sur un objet.

DNATranscriber: any = {
    "G":"C",
    "C":"G",
    "T":"A",
    "A":"U"
}

Cela fonctionne comme prévu. Je voudrais maintenant convertir ceci en tapuscrit.

DNATranscriber = {
       "G":"C",
       "C": "G",
       "T": "A",
       "A": "U"
    }

Mais j'obtiens l'erreur suivante.

L'élément a implicitement un type 'any' car l'expression de type 'string'> ne peut pas être utilisée pour indexer le type '{"A": string; } '. Aucune signature d'index avec un paramètre de type 'string' n'a été trouvée sur type> '{"A": string; } '. ts (7053)

Je pensais que le problème était que j'avais besoin que ma clé d'objet soit une chaîne. Mais les convertir en chaînes ne fonctionnait pas.

class Transcriptor {
    DNATranscriber = {
       G:"C",
       C: "G",
       T: "A",
       A: "U"
    }
    toRna(sequence: string) {
        const sequenceArray = [...sequence];
        const transcriptionArray = sequenceArray.map(character =>{
            return this.DNATranscriber[character];
        });
    }
}

export default Transcriptor

Je suis assez confus par cela. Il dit qu'aucune signature d'index avec un type de chaîne n'existe sur mon objet. Mais je suis sûr que c'est le cas. Qu'est-ce que je fais mal?

Edit - J'ai résolu ce problème en donnant à l'objet DNATranscriber un type de any.

DNATranscriber = {
    "G":"C",
    "C": "G",
    "T": "A",
    "A": "U"
}
function toRna(sequence){
    const sequenceArray = [...sequence];
    const transcriptionArray = sequenceArray.map(character =>{
        return this.DNATranscriber[character];
    });

    return transcriptionArray.join("");
}

console.log(toRna("ACGTGGTCTTAA")); //Returns UGCACCAGAAUU


15 commentaires

ce n'est pas la réponse, mais vous avez oublié de renvoyer la valeur de toRna


Quelle est votre version dactylographiée? Je n'obtiens aucune erreur stackblitz.com/edit/angular-kupcve `


C'est la version "2.5.3"


J'ai essayé avec "3.1.1", il est peut-être temps de mettre à jour;)


Je rétrograde mon stackblitz à 2.5.3 et toujours pas d'erreur


Je fais en fait cela dans le cadre d'une formation sur exercism.io . Je ne suis pas sûr qu'il soit possible de mettre à jour la version dactylographiée.


Bizarre. Je n'ai aucune idée pourquoi cela ne fonctionne pas alors.


Il y avait aussi un problème étrange, dans es5 si vous étalez une chaîne comme [... string], le tableau de caractères sera renvoyé. dans mon stackblitz, il ne le faisait pas, donc j'ai dû utiliser Array.from (string) pour le faire fonctionner. Peut-être essayez-le également (copiez-collez le code de mon stackblitz)


J'ai essayé ça. J'ai toujours le même problème.


À quoi vous attendez-vous lorsque vous appelez toRNA("OBVIOUS NONSENSE") ?


Je l'ai corrigé. Je vais ajouter une mise à jour.


Bien sûr, tapez quelque chose comme any et cela résoudra le problème, de la même manière que retirer la batterie d'un détecteur de fumée corrige un incendie potentiel.


Votre métaphore est un peu maladroite mais je pense toujours que vous faites valoir un point très valable. Je vais y réfléchir et essayer de trouver une meilleure solution.


Aïe, vous m'avez frappé dans la métaphore. , En tout cas c'est comment je le ferais


Merci @jcalz. Je réfléchissais à la façon dont je pourrais incorporer des vérifications pour les entrées invalides. J'apprécie vraiment votre temps.


11 Réponses :


46
votes

Vous pouvez corriger les erreurs en validant votre saisie, ce que vous devez faire quoi qu'il en soit.

Les types suivants vérifient correctement, via des validations de protection de type

enum DNATranscriber {
    G = 'C',
    C = 'G',
    T = 'A',
    A = 'U'
}
export default function toRna(sequence: string) {
    const sequenceArray = [...sequence];
    validateSequence(sequenceArray);
    const transcribedRNA = sequenceArray.map(codon => DNATranscriber[codon]);
    return transcribedRNA;
}

function validateSequence(values: string[]): asserts codons is Array<keyof typeof DNATranscriber> {
    if (!values.every(isValidCodon)) {
        throw Error('invalid sequence');    
    }
}
function isValidCodon(value: string): value is keyof typeof DNATranscriber {
    return value in DNATranscriber;
}

Voici une version plus idiomatique

enum DNATranscriber {
    G = 'C',
    C = 'G',
    T = 'A',
    A = 'U'
}
export default function toRna(sequence: string) {
    const sequenceArray = [...sequence];
    if (!isValidSequence(sequenceArray)) {
        throw Error('invalid sequence');
    }
    const transcribedRNA = sequenceArray.map(codon => DNATranscriber[codon]);
    return transcribedRNA;
}

function isValidSequence(values: string[]): codons is Array<keyof typeof DNATranscriber> {
    return values.every(isValidCodon);
}
function isValidCodon(value: string): value is keyof typeof DNATranscriber {
    return value in DNATranscriber;
}

Remarquez comment nous exploitons une énumération de chaînes TypeScript pour améliorer la clarté et renforcer le typage des mappages de paires de base. Plus important encore, remarquez comment nous utilisons une function . C'est important! La conversion de JavaScript en TypeScript n'a rien à voir avec les classes, cela a à voir avec les types statiques.

Mettre à jour :

Depuis TypeScript 3.7, nous pouvons l'écrire de manière plus expressive, en formalisant la correspondance entre la validation d'entrée et son implication de type à l'aide de signatures d'assertion .

const DNATranscriber = {
    G: 'C',
    C: 'G',
    T: 'A',
    A: 'U'
};
export default class Transcriptor {
    toRna(sequence: string) {
        const sequenceArray = [...sequence];
        if (!isValidSequence(sequenceArray)) {
            throw Error('invalid sequence');
        }
        const transcribedRNA = sequenceArray.map(codon => DNATranscriber[codon]);
        return transcribedRNA;
    }
}

function isValidSequence(codons: string[]): codons is Array<keyof typeof DNATranscriber> {
    return codons.every(isValidCodon);
}
function isValidCodon(value: string): value is keyof typeof DNATranscriber {
    return value in DNATranscriber;
}

Vous pouvez en savoir plus sur les signatures d'assertion dans les notes de publication de TypeScript 3.7 .


2 commentaires

Comme alternative, est-il possible d'ajouter une signature d'index à DNATranscriber ? Étant donné que l'erreur indique "Typescript: No index signature with a parameter of type 'string' was found on type '{ “A”: string; }" , cela implique qu'il existe un moyen d'ajouter une signature d'index de type' string ' . Cela peut-il être fait?


Oui, vous pouvez le faire, mais le code ne serait pas de type sûr ou expressif de la manière prévue par la question. Il y a une raison pour laquelle il ne l'a pas écrit de cette façon, une bonne raison.



71
votes

Vous pouvez également faire ceci:

(this.DNATranscriber as DNA)[character];

Éditer.

Il est FORTEMENT recommandé de lancer l'objet avec le type approprié au lieu de any . Le cast d'un objet en tant que any objet vous aide uniquement à éviter les erreurs de type lors de la compilation du typographie, mais cela ne vous aide pas à garder votre code en sécurité.

Par exemple

interface DNA {
    G:"C",
    C: "G",
    T: "A",
    A: "U"
}

Et puis vous le lancez comme ceci:

(this.DNATranscriber as any)[character];


2 commentaires

Hé, je viens de faire ce que vous avez dit dans votre version d'édition. mais j'ai toujours une erreur


Un type d' DNA explicite n'est pas une mauvaise idée mais ne serait-ce pas. this.DNATranscriber alors être déclaré comme DNATranscriber: DNA rendant le "cast" redondant?



8
votes

J'ai résolu un problème similaire dans ma fonction getClass comme ceci:

import { ApiGateway } from './api-gateway.class';
import { AppSync } from './app-sync.class';
import { Cognito } from './cognito.class';

export type stackInstances = typeof ApiGateway | typeof  AppSync | typeof Cognito

export const classes = {
  ApiGateway,
  AppSync,
  Cognito
} as {
  [key: string]: stackInstances
};

export function getClass(name: string) {
  return classes[name];
}

Taper mes classes const avec mon type d'union a rendu le typographie heureux et cela a du sens pour moi.


0 commentaires

1
votes

J'ai joué avec ça pendant un moment. Voici mon scénario:

J'ai deux types, metrics1 et metrics2, chacun avec des propriétés différentes:

let myKey:string = '';
myKey = 'a';
if (isValidMetric(myKey, myMetrics)) {
    console.log(myMetrics[myKey]);
}

À un moment donné de mon code, j'ai créé un objet qui est l'intersection de ces deux types car cet objet contiendra toutes leurs propriétés:

function isValidMetric(prop: string, obj: metrics1 & metrics2): prop is keyof (metrics1 & metrics2) {
    return prop in obj;
}

Maintenant, j'ai besoin de référencer dynamiquement les propriétés de cet objet. C'est là que nous rencontrons des erreurs de signature d'index. Une partie du problème peut être décomposée en fonction de la vérification au moment de la compilation et de la vérification à l' exécution . Si je référence l'objet à l'aide d'un const , je ne verrai pas cette erreur car TypeScript peut vérifier si la propriété existe pendant la compilation:

const myKey = 'a';
console.log(myMetrics[myKey]); // No issues, TypeScript has validated it exists

Si, cependant, j'utilise une variable dynamique (par exemple, let ), alors TypeScript ne pourra pas vérifier si la propriété existe pendant la compilation, et nécessitera une aide supplémentaire pendant l'exécution. C'est là qu'intervient le typeguard suivant:

const myMetrics: metrics1 & metrics2 = {
    a: 10,
    b: 20,
    c: 30,
    d: 40,
    e: 50,
    f: 60
};

Cela se lit comme « Si l'obj a la propriété prop puis laissez tapuscrit savoir que prop existe dans l'intersection de metrics1 et metrics2. » Remarque : assurez-vous d'entourer metrics1 & metrics2 entre parenthèses après keyof comme indiqué ci-dessus, sinon vous vous retrouverez avec une intersection entre les clés de metrics1 et le type de metrics2 (pas ses clés).

Maintenant, je peux utiliser le typeguard et accéder en toute sécurité à mon objet pendant l'exécution:

type metrics1 = {
    a: number;
    b: number;
    c: number;
}

type metrics2 = {
    d: number;
    e: number;
    f: number;
}


0 commentaires

8
votes

N'utilisez aucun, utilisez des génériques

const getKeyValue = <T, K extends keyof T>(obj: T, key: K): T[K] => obj[key];

Mauvais - la raison de l'erreur est que le type d' object est juste un objet vide par défaut. Par conséquent, il n'est pas possible d'utiliser un type string pour indexer {} .

Mieux - la raison pour laquelle l'erreur disparaît est que maintenant nous disons au compilateur que l'argument obj sera une collection de paires chaîne / valeur ( string/any ). Cependant, nous utilisons le type any , donc nous pouvons faire mieux.

Best - T étend l'objet vide. U étend les clés de T Par conséquent, U existera toujours sur T , il peut donc être utilisé comme valeur de recherche.

Voici un exemple complet:

J'ai changé l'ordre des génériques ( U extends keyof T vient maintenant avant T extends object ) pour souligner que l'ordre des génériques n'est pas important et vous devez sélectionner un ordre qui convient le mieux à votre fonction.

const getKeyValue = <U extends keyof T, T extends object>(key: U) => (obj: T) =>
  obj[key];

interface User {
  name: string;
  age: number;
}

const user: User = {
  name: "John Smith",
  age: 20
};

const getUserName = getKeyValue<keyof User, User>("name")(user);

// => 'John Smith'

Syntaxe alternative

// bad
const _getKeyValue = (key: string) => (obj: object) => obj[key];
    
// better
const _getKeyValue_ = (key: string) => (obj: Record<string, any>) => obj[key];
    
// best
const getKeyValue = <T extends object, U extends keyof T>(key: U) => (obj: T) =>
      obj[key];


4 commentaires

Comment cela fonctionnerait-il si l'utilisateur avait une autre clé avec une interface en tant que type? J'obtiens une erreur indiquant qu'ils ne peuvent pas être affectés à «chaîne».


Voilà:codesandbox.io/s/recursing-sun-cundn?file=/src/index.ts


@AlexMckay Vous êtes un génie! Merci d'avoir répondu,


J'ai écrit un petit paquet npm avec cette fonction pour rendre cette tâche plus facile pour ceux qui sont nouveaux dans Typescript. Il fait 38 octets une fois minifié et contient un commentaire jsdoc, donc si vous survolez la fonction, il fournit la réponse ci-dessus.



7
votes

Vous avez deux options avec Typecript simple et idiomatique:

  1. Utiliser le type d'index
DNATranscriber: { G: string; C: string; T: string; A: string } = {
  G: "C",
  C: "G",
  T: "A",
  A: "U",
};

Il s'agit de la signature d'index dont parle le message d'erreur. Référence

  1. Tapez chaque propriété:
DNATranscriber: { [char: string]: string } = {
  G: "C",
  C: "G",
  T: "A",
  A: "U",
};


0 commentaires

0
votes

Pour toute personne aux prises avec des cas similaires

const key = getFirstType(dnaChain) as keyof typeof DNATranscriber;

essayer de l'utiliser avec des objets simples ( utilisés comme dictionnaires ) comme:

const key = getFirstType(dnaChain);
const result = DNATranscriber[key];

et en essayant d'accéder dynamiquement à la valeur à partir d'une clé calculée comme:

DNATranscriber = {
   G:"C",
   C: "G",
   T: "A",
   A: "U"
}

et vous avez rencontré l'erreur comme indiqué ci-dessus, vous pouvez utiliser l' opérateur keyof et essayer quelque chose comme

No index signature with a parameter of type 'string' was found on type X

vous aurez certainement besoin d'un garde au result mais si cela semble plus intuitif que certains types personnalisés magiques, c'est ok.


0 commentaires

13
votes

C'est ce que j'ai fait pour résoudre mon problème connexe

interface Map {
  [key: string]: string | undefined
}

const HUMAN_MAP: Map = {
  draft: "Draft",
}

export const human = (str: string) => HUMAN_MAP[str] || str


0 commentaires

0
votes

Cela éliminera l'erreur et est de type sécurisé:

this.DNATranscriber[character as keyof typeof DNATranscriber]


0 commentaires

1
votes

Pour ceux qui utilisent Google:

Aucune signature d'index avec un paramètre de type 'chaîne' n'a été trouvée sur le type ...

très probablement, votre erreur devrait se lire comme suit:

Vouliez-vous utiliser un type plus spécifique tel que keyof Number au lieu de string ?

J'ai résolu un problème de frappe similaire avec un code comme celui-ci:

const stringBasedKey = `SomeCustomString${someVar}` as keyof typeof YourTypeHere;

Ce problème m'a aidé à comprendre la vraie signification de l'erreur.


0 commentaires

0
votes

Voici l'exemple de fonction trim type générique d'objet tableau

const trimArrayObject = <T>(items: T[]) => {

  items.forEach(function (o) {

    for (let [key, value] of Object.entries(o)) {

      const keyName = <keyof typeof o>key;

      if (Array.isArray(value)) {

        trimArrayObject(value);

      } else if (typeof o[keyName] === "string") {

        o[keyName] = value.trim();

      }

    }

  });

};


0 commentaires