我有一些香草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)
我认为问题是我需要我的对象键是一个字符串。但是将它们转换为字符串并不管用。
DNATranscriber = {
"G":"C",
"C": "G",
"T": "A",
"A": "U"
}
我对此很困惑。它表示在我的对象上不存在具有字符串类型的索引签名。但我确信它确实如此。我做错了什么?
编辑-我通过给DNATranscriber对象一个any类型来解决这个问题。
DNATranscriber: any = {
"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的交集中。”注意:确保将metrics1和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];
坏-错误的原因是对象类型默认为空对象。因此,不可能使用字符串类型来索引{}。
更好的是,错误消失的原因是因为现在我们告诉编译器obj参数将是字符串/值(string/any)对的集合。然而,我们正在使用任何类型,所以我们可以做得更好。
Best - 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];