typescript:在类型“{“A”:字符串;}

IT技术 javascript typescript
2021-03-17 11:48:05

我有一些普通的 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": string; }'。在 type >'{ "A": string; 上找不到带有类型为“string”的参数的索引签名 }'.ts(7053)

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

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

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

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

DNATranscriber: any = {
    "G":"C",
    "C":"G",
    "T":"A",
    "A":"U"
}
6个回答

此外,您可以这样做:

(this.DNATranscriber as any)[character];

编辑。

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

例如

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

然后你像这样投射它:

(this.DNATranscriber as DNA)[character];
嘿,我只是按照您在编辑版本中所说的做了。但仍然有错误
2021-04-21 11:48:05
显式DNA类型不是一个坏主意,但不会this.DNATranscriberDNATranscriber: DNA使“强制转换”变得多余那样声明
2021-05-15 11:48:05

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

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

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 来提高清晰度并在不更改实现的情况下获得更强大的碱基对映射类型。我们使用 a 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 发行说明 中阅读有关断言签名的更多信息

是的,您可以这样做,但是代码将不会以问题预期的方式进行类型安全或表达。他没有那样写是有原因的,一个很好的理由。
2021-04-21 11:48:05
作为替代方案,是否可以将索引签名添加到DNATranscriber? 由于错误为"Typescript: No index signature with a parameter of type 'string' was found on type '{ “A”: string; }",这意味着有一种方法可以添加“字符串”类型的索引签名。这能做到吗?
2021-04-23 11:48:05

这是我为解决相关问题所做的工作

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

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

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

不要使用任何,使用泛型

// 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];
用这个函数写了一个很小的npm 包,让那些不熟悉 Typescript 的人更容易完成这项任务。缩小后它是 38 个字节,并包含一个 jsdoc 注释,因此如果您将鼠标悬停在该函数上,它会提供上面的答案。
2021-04-17 11:48:05
为 getKeyValue使用建议的替代语法时,我需要将 getUserName 行修改为如下所示:const getUserName = getKeyValue<User, keyof User>(user, "name");
2021-04-17 11:48:05
如果用户有另一个带有原样接口的键,这将如何工作?我收到一个错误,提示它们不能分配给“字符串”。
2021-04-29 11:48:05

您有两个简单且惯用的 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",
};
第一个选项比定义接口更容易。我喜欢
2021-04-17 11:48:05
经过数小时的搜索和思考,我发现这个解决方案是最简单的。整体keyof或索引类型查询的概念真的很混乱。我希望有人可以用简单的英语解释它,而无需使用像T之类的神秘字母K使用一个真实的例子。
2021-04-18 11:48:05