[javascript] Typescript : ‘{ “A”: string; 유형에서 매개 변수가’string ‘인 색인 서명이 없습니다. }

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

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



답변

입력의 유효성을 검사하여 오류를 수정할 수 있으며, 이는 물론 수행해야하는 작업입니다.

다음은 유형 보호 유효성 검사를 통해 올바르게 유형 검사합니다.

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 릴리스 정보 에서 어설 션 서명 에 대한 자세한 내용을 읽을 수 있습니다 .


답변

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

(this.DNATranscriber as any)[character];

편집하다.

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

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

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

(this.DNATranscriber as DNA)[character];


답변

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

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

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

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


답변

어떤 것도 사용하지 말고 제네릭을 사용하십시오.

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


답변

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가 행복 해졌고 나에게 의미가 있습니다.


답변

단순하고 관용적 인 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",
};


답변

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

각각 다른 속성을 가진 두 가지 유형, 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]);
}