Машинопис: На типі '{“A”: рядок не знайдено підпису індексу з параметром типу' string '; }


87

У мене є ванільний код javascript, який приймає рядок, розбиває рядок на символи, а потім узгоджує ці символи з ключем на об’єкті.

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

Це працює, як очікувалося. Зараз я хотів би перетворити це на машинопис.

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

Але я отримую таку помилку.

Елемент неявно має тип "будь-який", оскільки вираз типу "рядок"> не може використовуватися для індексації типу "{" А ": рядок; } '. Не знайдено підпису індексу з параметром типу 'string' у type> '{"A": string; } '. ts (7053)

Я думав, що проблема полягає в тому, що мені потрібен мій ключ об’єкта, щоб бути рядком. Але перетворити їх на рядки не вдалося.

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

Мене це дуже бентежить. Там сказано, що на моєму об’єкті не існує підпису індексу із типом рядка. Але я впевнений, що це так. Що я роблю не так?

Редагувати - Я вирішив це, надавши об’єкту DNATranscriber тип будь-якого.

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

це не відповідь, але ви забули повернути значення зtoRna
Реза,

Яка ваша версія машинопису? Я не отримую жодної помилки stackblitz.com/edit/angular-kupcve `
Реза

Це версія "2.5.3"
onTheInternet

49
Звичайно, введіть щось як, anyі це виправить, так само, як витяг акумулятора з димового детектора фіксує потенційну пожежу.
jcalz

1
Ой, ти вдарив мене прямо в метафорі. У всякому разі, це те , як я б це зробити
jcalz

Відповіді:


50

Ви можете виправити помилки, перевіривши свої дані, що потрібно робити незалежно від курсу.

Наступні типи перевіряються правильно за допомогою перевірки типу захисту

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

Ось більш ідіоматична версія

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

Зверніть увагу, як ми використовуємо перерахування рядків TypeScript для поліпшення чіткості та посилення друку базових парних відображень. Що ще важливіше, зауважте, як ми використовуємо a function. Це важливо! Перетворення JavaScript у TypeScript не має нічого спільного з класами, воно пов’язане зі статичними типами.

Оновлення :

Починаючи з TypeScript 3.7, ми можемо писати це більш виразно, формалізуючи відповідність між перевіркою введення та імплікацією його типу за допомогою підписів твердження .

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

Ви можете прочитати більше про підписи тверджень у примітках до випуску TypeScript 3.7 .


1
Як альтернативу, чи можна додати підпис індексу DNATranscriber? Оскільки помилка говорить "Typescript: No index signature with a parameter of type 'string' was found on type '{ “A”: string; }", це означає, що існує спосіб додати підпис індексу типу 'рядок'. Чи можна це зробити?
Камерон Хадсон,

Так, ви могли б це зробити, але тоді код не був би безпечним або виразним для набору типу, як передбачалося питання. Є причина, що він не написав це так, поважна причина.
Алуан Хаддад

На rhs validateSequence ...:ви пишете, codonsале ім'я параметра - values. Зазвичай це не проблема, зараз вже достатньо рівні опосередкованості ...
Крейг Хікс,

76

Крім того, ви можете зробити це:

(this.DNATranscriber as any)[character];

Редагувати.

Це настійно рекомендується перетворити об'єкт з відповідним типом замість any. Передача об’єкта як anyлише допоможе вам уникнути помилок типу під час компіляції TypeScript, але це не допоможе вам захистити ваш тип коду.

Напр

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

А потім ви робите це так:

(this.DNATranscriber as DNA)[character];

1
Привіт, я щойно зробив те, що ти сказав у редакції. але все-таки отримала помилку
Archsx

1
Явний DNAтип - це не погана ідея, але чи не буде this.DNATranscriberвін тоді оголошений як DNATranscriber: DNAстворення зайвого?
Алуан Хаддад,

18

Це було те, що я зробив, щоб вирішити пов’язану з цим проблему

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

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

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


Просто ідеально! Дуже елегантно
Рахул Пурохіт,

12

Не використовуйте будь-які, використовуйте дженерики

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

Погано - причиною помилки є objectтип, це лише порожній об'єкт за замовчуванням. Тому неможливо використовувати stringтип для індексації{} .

Краще - причина зникнення помилки полягає в тому, що зараз ми говоримо компілятору, що objаргументом буде колекція string/anyпар string / value ( ). Однак ми використовуємоany тип, тому можемо зробити краще.

Найкраще - Tрозширює порожній об'єкт. Uрозширює клавіші T. Тому він Uбуде існувати завжди T, тому його можна використовувати як значення пошуку.

Ось повний приклад:

Я змінив порядок загальних препаратів ( U extends keyof Tтепер це раніше T extends object), щоб підкреслити, що порядок загальних препаратів не є важливим, і ви повинні вибрати порядок, який має найбільший сенс для вашої функції.

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'

Альтернативний синтаксис

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

Як би це працювало, якби користувач мав інший ключ із таким інтерфейсом, як тип? Я отримую помилку, що їх неможливо віднести до "рядка".
lockykeaney


@AlexMckay Ти геній! Дякую за відповідь,
Авінаш

Я написав крихітний пакет npm з цією функцією, щоб полегшити це завдання тим, хто не є новим у Typescript. Одноразово зменшено 38 байт і містить коментар jsdoc, тому, якщо навести курсор на функцію, вона надасть відповідь вище.
Alex Mckay,

9

Я вирішив подібну проблему у своїй getClassфункції так:

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

Введення мого classesconst із типом union зробило машинопис щасливим, і це для мене має сенс.


8

У вас є два варіанти використання простої та ідіоматичної машинопису:

  1. Використовуйте тип індексу
DNATranscriber: { [char: string]: string } = {
  G: "C",
  C: "G",
  T: "A",
  A: "U",
};

Це підпис індексу, про який йде повідомлення про помилку. Довідково

  1. Введіть кожну властивість:
DNATranscriber: { G: string; C: string; T: string; A: string } = {
  G: "C",
  C: "G",
  T: "A",
  A: "U",
};

Перший варіант простіший, ніж визначення інтерфейсу. Мені це подобається
drlingyi

1

Я певний час возився з цим. Ось мій сценарій:

У мене є два типи, метрики1 і метрики2, кожен з різними властивостями:

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

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

У певний момент мого коду я створив об’єкт, який є перетином цих двох типів, оскільки цей об’єкт буде зберігати всі їх властивості:

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

Тепер мені потрібно динамічно посилатися на властивості цього об’єкта. Тут ми стикаємося з помилками підпису індексу. Частина проблеми може бути розбита на основі перевірки часу компіляції та перевірки часу виконання . Якщо я посилаюся на об'єкт за допомогою const , я не побачу цієї помилки, оскільки TypeScript може перевірити, чи існує властивість під час компіляції:

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

Якщо, однак, я використовую динамічну змінну (наприклад, let ), то TypeScript не зможе перевірити, чи існує властивість під час компіляції, і потребуватиме додаткової допомоги під час виконання. Ось де входить такий захисний щиток:

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

Це звучить так: "Якщо obj має властивість prop, повідомте про це TypeScript prop існує на перетині metrics1 & metrics2." Примітка : переконайтеся, що метрики1 і метрики2 оточені в дужки після keyof як показано вище, інакше ви отримаєте перетин між клавішами metrics1 і типом metrics2 (а не його ключами).

Тепер я можу використовувати typeguard і безпечно отримати доступ до свого об’єкта під час виконання:

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

1

Для тих, хто Google:

На типі не знайдено підпису індексу з параметром типу 'string' ...

швидше за все, ваша помилка повинна виглядати так:

Ви мали на увазі використовувати більш конкретний тип, наприклад keyof Number замість string?

Я вирішив подібну проблему набору тексту з таким кодом:

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

Це питання допомогло мені дізнатися справжній сенс помилки.


1

Вирішив подібну проблему, виконавши це:

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]

Чи можете ви додати пояснення щодо того, що робить цей блок коду?
Rastalamm

0

Для тих, хто бореться з подібними справами

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

намагаються використовувати його з простими об’єктами ( використовуються як дикти ), наприклад:

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

і намагається динамічно отримати доступ до значення з обчисленого ключа, наприклад:

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

і ви зіткнулися з помилкою, як показано вище, ви можете використовувати оператор keyof і спробувати щось на зразок

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

звичайно, вам знадобиться охоронець, resultале якщо це здається більш інтуїтивним, ніж магія деяких користувацьких типів, це нормально.



0

Ось приклад функції trim загального типу об’єкта масиву

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

      }

    }

  });

};
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.