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
11 Réponses :
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 .
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.
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];
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?
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.
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; }
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'
// 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];
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.
Vous avez deux options avec Typecript simple et idiomatique:
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
DNATranscriber: { [char: string]: string } = { G: "C", C: "G", T: "A", A: "U", };
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.
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
Cela éliminera l'erreur et est de type sécurisé:
this.DNATranscriber[character as keyof typeof DNATranscriber]
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 destring
?
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.
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(); } } }); };
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.