Typescript:在类型“ {{A”:string;上未找到参数类型为” string”的索引签名;}


79

我有一些原始的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

但是我收到以下错误。

元素隐式具有“任意”类型,因为类型'string'的表达式不能用于索引类型'{“ A”:string; }'。在类型>'{“ 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
Reza

您的打字稿是什么版本?我没有得到任何错误stackblitz.com/edit/angular-kupcve `
礼萨

版本为“ 2.5.3”
onInternet

44
当然,键入某些内容any,它将进行修复,就像将电池从烟雾探测器中取出来修复潜在的火灾一样。
jcalz

1
哎呀,你在隐喻中打我。无论如何,就是我的做法
jcalz

Answers:


46

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

以下类型通过类型保护验证正确检查

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发行说明中阅读有关断言签名的更多信息。


1
作为替代方案,是否可以向中添加索引签名DNATranscriber?既然错误显示为"Typescript: No index signature with a parameter of type 'string' was found on type '{ “A”: string; }",则表示存在一种添加类型为“字符串”的索引签名的方法。能做到吗?
卡梅隆·哈德森

是的,您可以执行此操作,但是代码将无法按照问题的意图进行安全输入或表达。他没有那样写是有原因的,很好的理由。
阿兰·哈达德

69

另外,您可以执行以下操作:

(this.DNATranscriber as any)[character];

编辑。

HIGHLY建议您投的对象,具有正确的类型,而不是any。将对象强制转换为any只能帮助您避免在编译打字稿时避免键入错误,但不能帮助您保持代码的类型安全。

例如

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

然后将其像这样进行投射:

(this.DNATranscriber as DNA)[character];

1
嘿,我刚刚按照您在编辑版本中所说的做了。但仍然出现错误
Archsx

1
显式DNA类型不是一个坏主意,但是会不会this.DNATranscriberDNATranscriber: DNA使“ cast”成为多余的那样被声明?
Aluan哈达德

11

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

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

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

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


8

我在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使打字稿感到高兴,这对我来说很有意义。


7

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

7

不使用任何,使用泛型

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

如果用户有另一个带有类型的界面的键,这将如何工作?我收到一个错误,无法将它们分配给“字符串”。
lockykeaney


@AlexMckay你是个天才!感谢您的回答,
Avinash

我写了一个小 带有此功能npm软件包,以使Typescript新手可以轻松完成此任务。一旦缩小,它就是38个字节,并包含一个jsdoc注释,因此,如果将鼠标悬停在该函数上,它将提供上面的答案。
Alex Mckay

1

我把这个弄乱了一段时间。这是我的情况:

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

0

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

No index signature with a parameter of type 'string' was found on type X

尝试将其与简单对象(用作dict)一起使用

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对此有所警惕,但是如果它比某些自定义类型魔术更直观,那就可以了。



0

对于那些使用Google的用户:

在类型...上未找到带有“字符串”类型参数的索引签名。

您的错误最有可能显示为:

您是否要使用keyof Number 而不是更具体的类型string

我使用如下代码解决了类似的打字问题:

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

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.