Typescript: No index signature with a parameter of type 'string' was found on type { "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

但是我得到了如下错误。

元素隐式具有“any”类型,因为类型“string”>的表达式不能用于索引类型“{"A":字符串;}”。 在类型>'{" a ":字符串上没有找到具有类型为'string'的参数的索引签名;}’.ts (7053) < / p >

我认为问题是我需要我的对象键是一个字符串。但是将它们转换为字符串并不管用。

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

我对此很困惑。它表示在我的对象上不存在具有字符串类型的索引签名。但我确信它确实如此。我做错了什么?

编辑-我通过给DNATranscriber对象一个any类型来解决这个问题。

DNATranscriber: any = {
"G":"C",
"C":"G",
"T":"A",
"A":"U"
}
317173 次浏览

您可以通过验证输入来修复错误,这是您无论如何都应该做的事情。

通过类型保护验证,下面的类型检查是正确的

const DNATranscriber = {
G: 'C',
C: 'G',
T: 'A',
A: 'U'
};


export default class Transcriptor {
toRna(dna: string) {
const codons = [...dna];
if (!isValidSequence(codons)) {
throw Error('invalid sequence');
}
const transcribedRNA = codons.map(codon => DNATranscriber[codon]);
return transcribedRNA;
}
}


function isValidSequence(values: string[]): values is Array<keyof typeof DNATranscriber> {
return values.every(isValidCodon);
}
function isValidCodon(value: string): value is keyof typeof DNATranscriber {
return value in DNATranscriber;
}

值得一提的是,您似乎误解了将JavaScript转换为TypeScript需要使用类。

在下面的更习惯的版本中,我们利用TypeScript来提高清晰度,并在不改变实现的情况下获得更强的碱基对映射类型。我们使用function,就像原来的一样,因为它有意义。这很重要!将JavaScript转换为TypeScript与类无关,它与静态类型有关。

const DNATranscriber = {
G: 'C',
C: 'G',
T: 'A',
A: 'U'
};


export default function toRna(dna: string) {
const codons = [...dna];
if (!isValidSequence(codons)) {
throw Error('invalid sequence');
}
const transcribedRNA = codons.map(codon => DNATranscriber[codon]);
return transcribedRNA;
}


function isValidSequence(values: string[]): values is Array<keyof typeof DNATranscriber> {
return values.every(isValidCodon);
}
function isValidCodon(value: string): value is keyof typeof DNATranscriber {
return value in DNATranscriber;
}

更新:

从TypeScript 3.7开始,我们可以更有表现力地编写它,使用断言签名形式化输入验证及其类型暗示之间的对应关系。

const DNATranscriber = {
G: 'C',
C: 'G',
T: 'A',
A: 'U'
} as const;


type DNACodon = keyof typeof DNATranscriber;
type RNACodon = typeof DNATranscriber[DNACodon];


export default function toRna(dna: string): RNACodon[] {
const codons = [...dna];
validateSequence(codons);
const transcribedRNA = codons.map(codon => DNATranscriber[codon]);
return transcribedRNA;
}


function validateSequence(values: string[]): asserts values is DNACodon[] {
if (!values.every(isValidCodon)) {
throw Error('invalid sequence');
}
}
function isValidCodon(value: string): value is DNACodon {
return value in DNATranscriber;
}

你可以在TypeScript 3.7发布说明中阅读有关断言签名的更多信息。

此外,你还可以这样做:

(this.DNATranscriber as any)[character];

编辑。

高度建议使用正确的类型转换对象,而不是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];
}

用union类型输入classes const使typescript感到高兴,这对我来说是有意义的。

我摆弄了一段时间。这是我的设想:

我有两个类型,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
};

现在,我需要动态引用该对象的属性。这就是我们遇到索引签名错误的地方。部分问题可以根据编译时检查和运行时检查进行分解。如果我使用常量引用对象,我将不会看到这个错误,因为TypeScript可以在编译时检查该属性是否存在:

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

然而,如果我使用的是动态变量(例如),那么TypeScript将无法在编译时检查该属性是否存在,并将在运行时需要额外的帮助。这就是下面的typeguard的用武之地:

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

这句话的意思是:“如果obj具有属性道具,那么让TypeScript知道道具存在于度量1 &metrics2。”请注意:确保你围绕着参数1 &如上面所示,将metrics2放在keyof后面的括号中,否则你将得到metrics1的键和metrics2的类型之间的交集(而不是它的键)。

现在,我可以使用typeguard并在运行时安全地访问我的对象:

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

不要使用任何,使用泛型

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

你有两个选择使用简单和惯用的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",
};

对于任何遇到类似情况的人来说

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处设置一个守卫,但如果它看起来比一些自定义类型魔法更直观,那就可以了。

这就是我解决相关问题的方法

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


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


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


这将消除错误,并且是类型安全的:

this.DNATranscriber[character as keyof typeof DNATranscriber]

对于那些谷歌的人:

在类型…上没有找到参数类型为'string'的索引签名。

你的错误很可能是这样的:

你的意思是使用一个更具体的类型,如keyof Number 而不是string?< / p >

我用这样的代码解决了一个类似的输入问题:

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

这个问题帮助我了解了错误的真正含义。

下面是数组对象的泛型类型的函数示例

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


}


}


});


};

通过这样做解决了类似问题:

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]

这里有一个不使用对象键的解决方案:

function toRna(value: string): string {
return value.split('').map(ch => 'CGAU'['GCTA'.indexOf(ch)]).join('');
}


console.log(toRna('ACGTGGTCTTAA'));
\\UGCACCAGAAUU

您可以使用返回类型来获取,就像这样。

getAllProperties(SellRent: number) : Observable<IProperty[]>{
return this.http.get<IProperty[]>('data/properties.json').pipe(
map(data => {


const propertiesArray: Array<IProperty> = [];
for(const id in data){
if(data.hasOwnProperty(id) && data[id].SellRent === SellRent){
propertiesArray.push(data[id]);
}
}
return propertiesArray;
})
)

在你的params上,你必须定义keyOf Object

interface User {
name: string
age: number
}


const user: User = {
name: "someone",
age: 20
}


function getValue(key: keyof User) {
return user[key]
}

我的解决方案是

type DNATranscriber = {
G: string,
C: string,
T: string,
A: string,
}
type DNATanscriberIndex = {
[key: string]: string
}


let variableName:DNATanscriberIndex&DNATanscriber

DNATranscriber类型是为了Typescript能够引用字段,DNATanscriberIndex类型是为了将索引声明为字符串

我相信这个对你更好。

这样你就可以在输入参数时得到建议(在编辑器中尝试),还有一个强返回类型供以后使用。

截图的建议

还有灵感来自阿卢·哈达德的回答得到序列验证,但是更有效一点,因为验证是在转录循环内部进行的。

type DNAletter = 'G' | 'C' | 'T' | 'A';
type RNAletter = 'C' | 'G' | 'A' | 'U';


const DNATranscriber: { [key in DNAletter]: RNAletter } = {
G: 'C',
C: 'G',
T: 'A',
A: 'U'
};


// Return `RNAletter[]`
function toRna(sequence: string | string[] | DNAletter[]) {
return ([...sequence] as DNAletter[]).map(character => {
const transcribed = DNATranscriber[character];
if (transcribed === undefined)
throw Error(`Invalid character "${character}" in sequence`);
return transcribed;
});
}

编辑:从TS3.4开始,你可以使用const

我知道这是一个老问题,但TS提供了一种更简单的方法来输入你的问题,而不是被问到… 从TS3.4开始,现在这里最简单的方法是使用"作为const" 输入任何对象都不是正确的解决方案IMO

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

意味着ts现在知道这些键和值不会改变,因此可以通过infer进行评估。这意味着TS已经知道DNATranscriber["G"]将是"C"并且可以对输出代码进行检查,这是非常有用的。

以前……如玛丽丝的响应

type Keys = "G" | "C" | "T" | "A";
type values "C" | "G" | "A" | "U";
DNATranscriber: {[K in Keys]: values} = {
"G":"C",
"C": "G",
"T": "A",
"A": "U"
};

不理想,因为它没有表示映射的静态性质。

例如,您可以使用Record。

let DNATranscriber: Record<string, string> = {};

一个快速的解决方法就像下面这样,或者至少我是这样做的:

(obj as { [k in string]: any })[key]
const Translator : { [key: string]: string } = {
G: "C",
C: "G",
T: "A",
A: "U"
}
 

export function toRna(DNA:string) {


const Translate = [...DNA];
let Values = Translate.map((dna) => Translator[dna])
if (Validate(Values)) {return Values.join('')}
}


export function Validate(Values:string[]) : Boolean{
if (Values.join('') === "" || Values.join('').length !== Values.length) throw Error('Invalid input DNA.');
return true
}