web-dev-qa-db-ja.com

Typescript:タイプ 'string'のパラメーターを持つインデックス署名がタイプ '{"A":stringで見つかりませんでした。 }

文字列入力を受け取り、文字列を文字に分割し、それらの文字をオブジェクトのキーに一致させるバニラ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

これは期待どおりに機能します。これを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

しかし、次のエラーが発生します。

タイプ 'string'の式はタイプ '{"A"のインデックスに使用できないため、要素は暗黙的に' any 'タイプを持っています。 } '。タイプ 'string'のパラメーターを持つ索引シグニチャーがタイプ> '{"A":stringで見つかりませんでした。 } '。ts(7053)

問題は、オブジェクトキーを文字列にする必要があることだと思いました。しかし、それらを文字列に変換することはできませんでした。

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

私はこれにかなり混乱しています。文字列型のインデックスシグネチャがオブジェクトに存在しないことを示しています。しかし、確かにそうです。何が悪いのですか?

編集-私はDNATranscriberオブジェクトに任意のタイプを与えることでこれを解決しました。

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

入力を検証することでエラーを修正できます。これは、コースに関係なく行う必要があることです。

次の型チェックは、型保護の検証を介して正しく行われます

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以降、これをより表現力豊かに記述できるようになり、assertion signaturesを使用して、入力検証とその型の意味との間の対応を形式化します。

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

assertion signaturesの詳細については、 TypeScript 3.7リリースノート を参照してください。

21
Aluan Haddad

また、これを行うことができます:

(this.DNATranscriber as any)[character];

編集

[〜#〜] highly [〜#〜]anyではなく適切なタイプでオブジェクトをキャストすることをお勧めします。オブジェクトをanyとしてキャストすることは、TypeScriptのコンパイル時の型エラーの回避に役立つだけですが、コードを型安全に保つことはできません。

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

そして、次のようにキャストします。

(this.DNATranscriber as DNA)[character];

私は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が満足のいくものになります。

1
Mattijs

私はしばらくこれをいじりました。これが私のシナリオです:

私は2つのタイプ、metrics1とmetrics2があり、それぞれ異なるプロパティがあります。

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

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

私のコードのある時点で、これらの2つの型の共通部分であるオブジェクトを作成しました。このオブジェクトはすべてのプロパティを保持するためです。

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

次に、そのオブジェクトのプロパティを動的に参照する必要があります。ここで、インデックス署名エラーが発生します。問題の一部はcompile-timeチェックとruntimeチェックに基づいて分解できます。 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の交点に存在することを知らせます。」と読みます。 :上記のようにkeyofの後に、metrics1とmetrics2を括弧で囲んでください。そうしないと、metrics1のキーとmetrics2のタイプが交差することになります(そのキーではありません)。

これで、タイプガードを使用して、実行時にオブジェクトに安全にアクセスできます。

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