web-dev-qa-db-fra.com

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

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

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

Cela fonctionne comme prévu. Je voudrais maintenant convertir cela en TypeScript.

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

Mais je reçois 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 'chaîne' n'a été trouvée sur le type> '{"A": chaîne; } '. 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 n'a pas fonctionné.

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

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

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

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

Vous pouvez corriger les erreurs en validant votre saisie, ce que vous devez faire malgré tout.

Les vérifications de type suivantes sont correctes, via les validations de protection de type

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

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 obtenir un typage plus fort des mappages de paires de bases. Plus important encore, notez comment nous utilisons un function. C'est important! La conversion de JavaScript en TypeScript n'a rien à voir avec les classes, elle concerne les types statiques.

Mise à jour :

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

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

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

21
Aluan Haddad

Vous pouvez également faire ceci:

(this.DNATranscriber as any)[character];

Modifier.

Il est FORTEMENT recommandé de transtyper l'objet avec le type approprié au lieu de any. La conversion d'un objet en any vous aide uniquement à éviter les erreurs de type lors de la compilation de TypeScript, mais ne vous aide pas à conserver votre code en toute sécurité.

par exemple

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

Et puis vous le lancez comme ceci:

(this.DNATranscriber as DNA)[character];
46

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 mon classes const avec mon type d'union a fait le bonheur de TypeScript et cela a du sens pour moi.

1
Mattijs

Je me suis amusé avec ça pendant un moment. Voici mon scénario:

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

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

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

À un moment 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:

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

Maintenant, je dois 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 compilation vérification et exécution vérification. Si je référence l'objet en utilisant 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 aura besoin d'une aide supplémentaire pendant l'exécution. C'est là qu'intervient le typeguard suivant:

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

Cela se lit comme suit: "Si le obj a la propriété prop, faites savoir à TypeScript que prop existe à l'intersection de metrics1 et metrics2." Remarque: assurez-vous d'entourer les métriques1 et métriques2 entre parenthèses après keyof comme indiqué ci-dessus, sinon vous vous retrouverez avec une intersection entre les clés de métriques1 et le type de métriques2 (pas ses clés).

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

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