Mecanografiado: No se encontró ninguna firma de índice con un parámetro de tipo 'cadena' en el tipo '{“A”: cadena; }

86

Tengo un código JavaScript de vanilla que toma una entrada de cadena, divide la cadena en caracteres y luego hace coincidir esos caracteres con una clave en un objeto.

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

Esto funciona como se esperaba. Ahora me gustaría convertir esto a mecanografiado.

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

Pero recibo el siguiente error.

El elemento tiene implícitamente un tipo 'cualquier' porque la expresión del tipo 'cadena'> no se puede usar para indexar el tipo '{"A": cadena; } '. No se encontró ninguna firma de índice con un parámetro de tipo 'cadena' en el tipo> '{"A": cadena; } '. ts (7053)

Pensé que el problema era que necesitaba que mi clave de objeto fuera una cadena. Pero convertirlos en cadenas no funcionó.

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

Estoy bastante confundido por esto. Dice que no existe una firma de índice con un tipo de cadena en mi objeto. Pero estoy seguro de que sí. ¿Qué estoy haciendo mal?

Editar: resolví esto dando al objeto DNATranscriber un tipo de any.

DNATranscriber: any = {
    "G":"C",
    "C":"G",
    "T":"A",
    "A":"U"
}
En Internet
fuente
no es la respuesta, pero olvidó devolver el valor detoRna
Reza
¿Cuál es su versión mecanografiada? No recibo ningún error stackblitz.com/edit/angular-kupcve `
Reza
Es la versión "2.5.3"
enInternet el
48
Claro, escriba algo como anyy lo solucionará, de la misma manera que sacar la batería de un detector de humo soluciona un posible incendio.
jcalz
1
Ay, me pegaste en la metáfora. De todos modos, así es como lo haría
jcalz

Respuestas:

50

Puede corregir los errores validando su entrada, que es algo que debe hacer independientemente, por supuesto.

El siguiente tipo verifica correctamente, a través de validaciones de protección de tipo

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

Aquí hay una versión más idiomática.

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

Observe cómo aprovechamos una enumeración de cadenas de TypeScript para mejorar la claridad y obtener una escritura más sólida de las asignaciones de pares de bases. Más importante aún, observe cómo usamos un function. ¡Esto es importante! La conversión de JavaScript a TypeScript no tiene nada que ver con las clases, tiene que ver con los tipos estáticos.

Actualización :

Desde TypeScript 3.7, podemos escribir esto de manera más expresiva, formalizando la correspondencia entre la validación de entrada y su implicación de tipo utilizando firmas de aserción .

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

Puede leer más sobre las firmas de aserción en las notas de la versión de TypeScript 3.7 .

Aluan Haddad
fuente
1
Como alternativa, ¿es posible agregar una firma de índice a DNATranscriber? Como dice el error "Typescript: No index signature with a parameter of type 'string' was found on type '{ “A”: string; }", implica que hay una manera de agregar una firma de índice de tipo 'cadena'. Se puede hacer esto?
Cameron Hudson
Sí, podría hacer eso, pero entonces el código no sería seguro o expresivo de la manera que pretendía la pregunta. Hay una razón por la que no lo escribió de esa manera, una buena razón.
Aluan Haddad
En el lado derecho de validateSequence ...:escribe, codonspero el nombre del parámetro es values. Normalmente no es un problema, ya hay suficientes niveles de indirección ...
Craig Hicks
76

Además, puedes hacer esto:

(this.DNATranscriber as any)[character];

Editar.

Se recomienda ALTAMENTE que lances el objeto con el tipo adecuado en lugar de any. Lanzar un objeto como anysolo le ayuda a evitar errores de tipo al compilar mecanografiado, pero no le ayuda a mantener su código seguro.

P.ej

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

Y luego lo lanzas así:

(this.DNATranscriber as DNA)[character];
Leonardo Emilio Domínguez
fuente
1
Oye, acabo de hacer lo que dijiste en tu versión de edición. pero todavía tengo el error
Archsx
1
Un DNAtipo explícito no es una mala idea, pero ¿no this.DNATranscriberse declararía entonces como si DNATranscriber: DNAel "elenco" fuera redundante?
Aluan Haddad
18

Esto fue lo que hice para resolver mi problema relacionado

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

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

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

Manoel Quirino Neto
fuente
¡Simplemente perfecto! Muy elegante
Rahul Purohit
11

No use ninguno, use genéricos

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

Malo: el motivo del error es que el objecttipo es solo un objeto vacío de forma predeterminada. Por lo tanto, no es posible utilizar un stringtipo para indexar {}.

Mejor: la razón por la que el error desaparece es porque ahora le estamos diciendo al compilador que el objargumento será una colección de string/anypares cadena / valor ( ). Sin embargo, estamos usando el anytipo, por lo que podemos hacerlo mejor.

Mejor: Textiende el objeto vacío. Uextiende las claves de T. Por Ulo tanto , siempre existirá T, por lo que se puede utilizar como valor de búsqueda.

Aquí hay un ejemplo completo:

Cambié el orden de los genéricos ( U extends keyof Tahora viene antes T extends object) para resaltar que el orden de los genéricos no es importante y debe seleccionar un orden que tenga más sentido para su función.

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'

Sintaxis alternativa

const getKeyValue = <T, K extends keyof T>(obj: T, key: K): T[K] => obj[key];
Alex Mckay
fuente
¿Cómo funcionaría esto si el usuario tuviera otra clave con una interfaz como la del tipo? Recibo un error de que no se pueden asignar a 'cadena'.
lockykeaney
@AlexMckay ¡Eres un genio! Gracias por la respuesta,
Avinash
Escribí un pequeño paquete npm con esta función para facilitar esta tarea a aquellos que son nuevos en Typecript. Tiene 38 bytes una vez minificado y contiene un comentario jsdoc, por lo que si pasa el mouse sobre la función, proporciona la respuesta anterior.
Alex Mckay
9

Resolví un problema similar en mi getClassfunción como este:

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

Escribir mi classesconstante con mi tipo de unión hizo feliz a la mecanografía y tiene sentido para mí.

Mattijs
fuente
8

Tienes dos opciones con TypeScript simple e idiomático:

  1. Usar tipo de índice
DNATranscriber: { [char: string]: string } = {
  G: "C",
  C: "G",
  T: "A",
  A: "U",
};

Esta es la firma de índice de la que habla el mensaje de error. Referencia

  1. Escriba cada propiedad:
DNATranscriber: { G: string; C: string; T: string; A: string } = {
  G: "C",
  C: "G",
  T: "A",
  A: "U",
};
Ventas Flávio Teixeira
fuente
La primera opción es más fácil que definir una interfaz. Me gusta
drlingyi
1

Me metí con esto por un tiempo. Aquí estaba mi escenario:

Tengo dos tipos, métricas1 y métricas2, cada uno con propiedades diferentes:

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

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

En un punto de mi código, creé un objeto que es la intersección de estos dos tipos porque este objeto contendrá todas sus propiedades:

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

Ahora, necesito hacer referencia dinámicamente a las propiedades de ese objeto. Aquí es donde nos encontramos con errores de firma de índice. Parte del problema se puede desglosar en función de la verificación en tiempo de compilación y la verificación en tiempo de ejecución . Si hago referencia al objeto usando una constante , no veré ese error porque TypeScript puede verificar si la propiedad existe durante el tiempo de compilación:

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

Sin embargo, si estoy usando una variable dinámica (por ejemplo, let ), TypeScript no podrá verificar si la propiedad existe durante el tiempo de compilación y requerirá ayuda adicional durante el tiempo de ejecución. Ahí es donde entra en juego el siguiente tipo de protección:

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

Esto se lee como, "Si el obj tiene la propiedad prop, entonces déjele saber a TypeScript que la prop existe en la intersección de metrics1 y metrics2". Nota : asegúrese de rodear metrics1 y metrics2 entre paréntesis después de keyof como se muestra arriba, o terminará con una intersección entre las claves de metrics1 y el tipo de metrics2 (no sus claves).

Ahora, puedo usar Typeguard y acceder de forma segura a mi objeto durante el tiempo de ejecución:

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

Para aquellos que buscan en Google:

No se encontró ninguna firma de índice con un parámetro de tipo 'cadena' en el tipo ...

lo más probable es que su error deba leerse como:

¿Quería utilizar un tipo más específico como en keyof Number lugar de string?

Resolví un problema de escritura similar con un código como este:

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

Este problema me ayudó a conocer el significado real del error.

Pavot
fuente
0

Para cualquiera que tenga problemas con casos similares

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

tratando de usarlo con objetos simples ( usados ​​como dictados ) como:

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

e intentar acceder dinámicamente al valor de una clave calculada como:

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

y se enfrentó al error como se muestra arriba, puede usar el operador keyof e intentar algo como

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

ciertamente necesitará un guardia en el resultpero si parece más intuitivo que algunos tipos personalizados de magia, está bien.

Igneel64
fuente
0

Esto eliminará el error y es seguro de tipos:

this.DNATranscriber[character as keyof typeof DNATranscriber]
Almeno Soares
fuente
0

Aquí está el ejemplo de función recortar tipo genérico de objeto de matriz

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();

      }

    }

  });

};
Thomas Yeh
fuente
0

Problema similar resuelto haciendo esto:

export interface IItem extends Record<string, any> {
    itemId: string;
    price: number;
}

const item: IItem = { itemId: 'someId', price: 200 };
const fieldId = 'someid';

// gives you no errors and proper typing
item[fieldId]
Eugene Kriulin
fuente
¿Puede agregar una explicación sobre lo que hace este bloque de código?
Rastalamm