Typescript : '{ "A": string; 유형에서 매개 변수가'string '인 색인 서명이 없습니다. }


87

문자열 입력을 받아 문자열을 문자로 분할 한 다음 해당 문자를 객체의 키에 일치시키는 바닐라 자바 ​​스크립트 코드가 있습니다.

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

이것은 예상대로 작동합니다. 이제 이것을 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

하지만 다음과 같은 오류가 발생합니다.

'문자열'유형의 표현식은 '{ "A"유형을 색인화하는 데 사용할 수 없기 때문에 요소가 암시 적으로'모든 '유형을 갖습니다. 문자열; } '. 유형> '{ "A": string에서'string '유형의 매개 변수를 가진 색인 서명이 없습니다. } '. ts (7053)

문제는 객체 키가 문자열이어야한다는 점이라고 생각했습니다. 그러나 그것들을 문자열로 변환하는 것은 작동하지 않았습니다.

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

나는 이것에 대해 꽤 혼란 스럽습니다. 내 개체에 문자열 유형의 인덱스 서명이 없다고 말합니다. 그러나 나는 그것을 확신합니다. 내가 도대체 ​​뭘 잘못하고있는 겁니까?

편집-DNATranscriber 객체에 어떤 유형이든 제공하여이 문제를 해결했습니다.

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

대답은 아니지만 값을 반환하는 것을 잊었습니다toRna
Reza

타이프 스크립트 버전은 무엇입니까? 나는 오류하지 않는 stackblitz.com/edit/angular-kupcve `
레자

버전 "2.5.3"
onTheInternet

51
물론 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 문자열 열거를 활용하여 명확성을 높이고 기본 쌍 매핑의 입력을 강화하는 방법에 주목하십시오. 더 중요한 것은 우리가 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; }"'문자열'유형의 색인 서명을 추가하는 방법이 있음을 의미합니다. 할 수 있습니까?
Cameron Hudson

예, 그렇게 할 수는 있지만 코드는 질문이 의도 한 방식으로 형식 안전하거나 표현 적이 지 않습니다. 그가 그렇게 쓰지 않은 이유가 있습니다. 그럴만 한 이유가 있습니다.
Aluan Haddad

validateSequence ...:당신 의 rhs에 작성 codons하지만 매개 변수 이름은 values. 이미 간접 충분한 수준 ... 거기에 일반적으로 문제가되지 않습니다
크레이그 힉스

76

또한 다음을 수행 할 수 있습니다.

(this.DNATranscriber as any)[character];

편집하다.

그건 높게 대신의 적절한 유형의 객체를 캐스팅하는 것이 좋습니다 any. 객체를 캐스트하면 anytypescript를 컴파일 할 때 유형 오류를 피하는 데 도움이되지만 코드 유형을 안전하게 유지하는 데는 도움이되지 않습니다.

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

그리고 다음과 같이 캐스팅합니다.

(this.DNATranscriber as DNA)[character];

1
방금 편집 버전에서 말한대로했습니다. 하지만 여전히 오류가 있습니다
Archsx

1
명시 적 DNA유형은 나쁜 생각은 아니지만 "캐스트"를 중복으로 만드는 this.DNATranscriber것처럼 선언 되지 DNATranscriber: DNA않을까요?
Aluan 하다드

18

이것은 내 관련 문제를 해결하기 위해 한 일이었습니다.

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

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

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


완벽 해! 아주 우아한
라훌 인 Purohit

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) 쌍 의 모음이 될 것이라고 말하고 있기 때문 입니다. 그러나 우리는 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 당신은 천재입니다! 대답을 주셔서 감사합니다,
Avinash

Typescript를 처음 접하는 사람들이이 작업을 더 쉽게 할 수 있도록이 함수를 사용 하여 작은 npm 패키지 를 작성했습니다. 축소되면 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];
}

classes내 조합 유형으로 내 const를 입력 하면 typescript가 행복 해졌고 나에게 의미가 있습니다.


8

단순하고 관용적 인 Typescript에는 두 가지 옵션이 있습니다.

  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

나는 이것을 잠시 동안 엉망으로 만들었다. 내 시나리오는 다음과 같습니다.

각각 다른 속성을 가진 두 가지 유형, metrics1 및 metrics2가 있습니다.

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는 컴파일 시간 동안 속성이 존재하는지 확인할 수 없으며 런타임 동안 추가 도움말이 필요합니다. 이것이 다음 typeguard가 들어오는 곳입니다.

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

이것은 다음과 같이 읽습니다. " obj에 prop 속성이있는 경우 TypeScript에 prop 이 metrics1 및 metrics2의 교차점에 있음을 알립니다 ." 참고 : 위와 같이 keyof 뒤에 metrics1 및 metrics2를 괄호로 묶어야합니다. 그렇지 않으면 metrics1의 키와 metrics2의 유형 (키가 아님)이 교차하게됩니다.

이제 typeguard를 사용하고 런타임 중에 내 개체에 안전하게 액세스 할 수 있습니다.

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

1

Google 사용자를 위해 :

'문자열'유형의 매개 변수가있는 색인 서명이 유형에서 발견되지 않았습니다.

대부분의 오류는 다음과 같이 읽어야합니다.

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

다음과 같은 간단한 객체 ( dicts로 사용됨 )와 함께 사용하려고합니다 .

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

이렇게하면 오류가 제거되고 형식이 안전합니다.

this.DNATranscriber[character as keyof typeof DNATranscriber]

0

다음은 배열 객체의 일반 유형을 트림하는 함수 예제입니다.

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

      }

    }

  });

};
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.