我有一些普通的 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
这按预期工作。我现在想将其转换为打字稿。
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”:字符串; }'。在 type >'{ "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
返回值
any
即可解决问题,就像从烟雾探测器中取出电池修复潜在火灾一样。
此外,您可以这样做:
(this.DNATranscriber as any)[character];
编辑。
强烈建议您使用正确的类型而不是 any
强制转换对象。将对象转换为 any
只能帮助您在编译 typescript 时避免类型错误,但不能帮助您保持代码类型安全。
例如
interface DNA {
G: "C",
C: "G",
T: "A",
A: "U"
}
然后你像这样投射它:
(this.DNATranscriber as DNA)[character];
您可以通过验证您的输入来修复错误,这是您无论如何都应该做的事情。
以下类型检查正确,通过类型保护验证
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 release notes 中阅读有关 断言签名 的更多信息。
DNATranscriber
添加索引签名?由于错误显示为 "Typescript: No index signature with a parameter of type 'string' was found on type '{ “A”: string; }"
,这意味着有一种方法可以添加“字符串”类型的索引签名。这可以做到吗?
keyof typeof
很有帮助!
这就是我为解决相关问题所做的
interface Map {
[key: string]: string | undefined
}
const HUMAN_MAP: Map = {
draft: "Draft",
}
export const human = (str: string) => HUMAN_MAP[str] || str
您有两个使用简单且惯用的 Typescript 的选项:
使用索引类型
DNATranscriber: { [char: string]: string } = {
G: "C",
C: "G",
T: "A",
A: "U",
};
这是错误消息正在谈论的索引签名。 Reference
键入每个属性:
DNATranscriber: { G: string; C: string; T: string; A: string } = {
G: "C",
C: "G",
T: "A",
A: "U",
};
keyof
或索引类型查询概念确实令人困惑。我希望有人能用简单的英语解释它,而不使用像 T
或 K
这样的神秘字母。使用一个真实的例子。
不要使用任何,使用泛型
// 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];
const getUserName = getKeyValue<User, keyof User>(user, "name");
这将消除错误并且是类型安全的:
this.DNATranscriber[character as keyof typeof DNATranscriber]
character
具有在编译时无法知道的动态值,则不能保证它始终是 keyof typeof DNATranscriber
类型。在此处使用带有 as
的类型断言会引入类型安全错误。
在您的 params
上,您必须定义 keyOf Object
。
interface User {
name: string
age: number
}
const user: User = {
name: "someone",
age: 20
}
function getValue(key: keyof User) {
return user[key]
}
通过这样做解决了类似的问题:
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]
Record<string, any>
扩展 IItem
,您允许对象包含具有 any
值的其他 string
键以及接口中定义的值。好的部分是您仍然可以自动完成已定义的属性。
我在 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 很开心,这对我来说很有意义。
对于那些使用谷歌的人:
在类型上找不到带有“字符串”类型参数的索引签名...
您的错误很可能应如下所示:
您的意思是使用更具体的类型,例如 keyof Number 而不是字符串?
我用这样的代码解决了类似的打字问题:
const stringBasedKey = `SomeCustomString${someVar}` as keyof typeof YourTypeHere;
This issue 帮助我了解了错误的真正含义。
我相信这个可能会更好地为您服务。
有了这个,您将在输入参数时获得建议(在编辑器中尝试),并为以后使用提供强大的返回类型。
https://i.stack.imgur.com/iULAa.png
此外,inspired by Aluan Haddad's answer,您可以获得序列验证,但效率更高,因为验证是在转录循环内部进行的。
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;
});
}
编辑:As of TS3.4 you can use as const
例如,您可以使用记录。
let DNATranscriber: Record<string, string> = {};
let DNATranscriber: { [key: string]: string } = {};
我搞砸了一段时间。这是我的场景:
我有两种类型,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 将无法在编译时检查该属性是否存在,并且在运行时需要额外的帮助。这就是以下类型保护的用武之地:
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]);
}
我的解决方案是
type DNATranscriber = {
G: string,
C: string,
T: string,
A: string,
}
type DNATanscriberIndex = {
[key: string]: string
}
let variableName:DNATanscriberIndex&DNATanscriber
DNATranscriber 类型用于 Typescript 能够引用字段,而 DNATanscriberIndex 类型用于将索引声明为字符串
这是修剪泛型数组对象的函数示例
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();
}
}
});
};
这是一个不使用对象键的解决方案:
function toRna(value: string): string {
return value.split('').map(ch => 'CGAU'['GCTA'.indexOf(ch)]).join('');
}
console.log(toRna('ACGTGGTCTTAA'));
\\UGCACCAGAAUU
你可以使用 type 一个返回类型来获取,就像这样。
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;
})
)
}
我知道这是一个老问题,但是 TS 现在提供了一种比询问时更简单的方法来输入您的问题... As of TS3.4,现在最简单的方法是使用“as const”正确的解决方案 IMO
DNATranscriber = {
"G":"C",
"C": "G",
"T": "A",
"A": "U"
} as const;
意味着 ts 现在知道这些键和值不会改变,因此可以通过推断进行评估。这意味着 TS 已经知道 DNATranscriber["G"] 将是 "C" 并且可以对输出代码进行检查,这更有帮助。
以前...如 Marias 的回复
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"
};
不理想,因为它不代表映射的静态性质。
一个快速的解决方法如下所示,或者至少我是如何做到的:
(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
}
对于任何在类似情况下苦苦挣扎的人
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 operator 并尝试类似
const key = getFirstType(dnaChain) as keyof typeof DNATranscriber;
当然,您将需要在 result
进行守卫,但如果它看起来比某些自定义类型的魔法更直观,那没关系。
不定期副业成功案例分享
DNA
类型不是一个坏主意,但不会像DNATranscriber: DNA
那样声明this.DNATranscriber
从而使“强制转换”变得多余吗?