【问题标题】:Typescript: No index signature with a parameter of type 'string' was found on type '{ "A": string; }打字稿:在类型'{“A”:字符串上找不到带有“字符串”类型参数的索引签名; }
【发布时间】:2019-10-27 08:02:45
【问题描述】:

我有一些普通的 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

但我收到以下错误。

元素隐含地具有'any'类型,因为'string'类型的表达式>不能用于索引类型'{“A”:string; }'。 在 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返回值
  • 你的打字稿版本是什么?我没有收到任何错误stackblitz.com/edit/angular-kupcve`
  • 当然,输入 any 即可解决问题,就像从烟雾探测器中取出电池修复潜在火灾一样。
  • 你的比喻有点笨拙,但我仍然认为你的观点非常有道理。我会考虑这个并尝试提出更好的解决方案。
  • 哎呀,你在比喻中击中了我。无论如何,this 是我的做法

标签: javascript typescript


【解决方案1】:

另外,您可以这样做:

(this.DNATranscriber as any)[character];

编辑。

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

例如

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

然后你像这样投射它:

(this.DNATranscriber as DNA)[character];

【讨论】:

  • 嘿,我只是按照你在编辑版本中所说的做了。但仍然有错误
  • 明确的DNA 类型不是一个坏主意,但this.DNATranscriber 不会像DNATranscriber: DNA 那样声明,从而使“演员”变得多余?
【解决方案2】:

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

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

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; }",这意味着有一种方法可以添加“字符串”类型的索引签名。这个可以吗?
  • 是的,你可以这样做,但是代码不会像问题所期望的那样是类型安全的或表达性的。他没有那样写是有原因的,一个很好的理由。
【解决方案3】:

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

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

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

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

【讨论】:

    【解决方案4】:

    不要使用任何,使用泛型

    // 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 package 来让那些刚接触 Typescript 的人更容易完成这项任务。缩小后为 38 个字节,并包含一个 jsdoc 注释,因此如果您将鼠标悬停在该函数上,它会提供上面的答案。
    • 当为 getKeyValue 使用建议的 替代语法 时,我需要将 getUserName 行修改为:const getUserName = getKeyValue&lt;User, keyof User&gt;(user, "name");
    【解决方案5】:

    你有两个选择简单和惯用的打字稿:

    1. 使用索引类型
    DNATranscriber: { [char: string]: string } = {
      G: "C",
      C: "G",
      T: "A",
      A: "U",
    };
    

    这是错误消息所涉及的索引签名。 Reference

    1. 键入每个属性:
    DNATranscriber: { G: string; C: string; T: string; A: string } = {
      G: "C",
      C: "G",
      T: "A",
      A: "U",
    };
    

    【讨论】:

    • 第一个选项比定义接口更容易。我喜欢它
    • 经过数小时的搜索和弯曲我的大脑,我发现这个解决方案是最简单的。整个keyof 或索引类型查询概念确实令人困惑。我希望有人能用简单的英语解释它,而不使用像 TK 这样的神秘字母。用一个真实的例子。
    【解决方案6】:

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

    this.DNATranscriber[character as keyof typeof DNATranscriber]
    

    【讨论】:

    • 这不是类型安全的。如果character 有一个在编译时无法知道的动态值,则不能保证它始终是keyof typeof DNATranscriber 类型。在这里使用带有as 的类型断言会引入类型安全错误。
    【解决方案7】:

    我在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 很开心,这对我来说很有意义。

    【讨论】:

      【解决方案8】:

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

      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&lt;string, any&gt; 扩展IItem,您允许对象包含具有any 值的其他string 键以及接口中定义的值。好的部分是您仍然可以自动完成已定义的属性。
      【解决方案9】:

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

      interface User {
          name: string
          age: number 
      }
      
      const user: User = {
          name: "someone",
          age: 20
      }
      
      function getValue(key: keyof User) {
          return user[key]
      }
      

      【讨论】:

        【解决方案10】:

        对于那些使用 Google 的人:

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

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

        您的意思是使用更具体的类型,例如keyof Number 而不是string?

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

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

        This issue 帮助我了解了错误的真正含义。

        【讨论】:

          【解决方案11】:

          我搞砸了一段时间。这是我的场景:

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

          【讨论】:

            【解决方案12】:

            我相信这个可能会更好地为您服务。

            有了这个,您将在输入参数时获得建议(在编辑器中尝试),以及一个强大的返回类型供以后使用。

            另外,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

            【讨论】:

              【解决方案13】:

              对于任何在类似情况下苦苦挣扎的人

              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 的守卫,但如果它看起来比一些自定义类型的魔法更直观,那没关系。

              【讨论】:

                【解决方案14】:

                这里是修剪泛型数组对象的函数示例

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

                【讨论】:

                  【解决方案15】:

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

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

                  【讨论】:

                    【解决方案16】:

                    你可以使用 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;
                      })
                    )
                    

                    }

                    【讨论】:

                      【解决方案17】:

                      我的解决办法是

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

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

                      【讨论】:

                        【解决方案18】:

                        我知道这是一个老问题,但 TS 现在提供了一种比被问到时更简单的方法来输入您的问题... As of TS3.4,现在最简单的方法是使用“as const” 将对象键入为 any 永远不是正确的解决方案 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"
                        };
                        

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

                        【讨论】:

                          猜你喜欢
                          • 2021-11-27
                          • 2020-01-06
                          • 1970-01-01
                          • 2021-10-16
                          • 2021-08-16
                          • 1970-01-01
                          • 1970-01-01
                          • 2021-12-04
                          • 1970-01-01
                          相关资源
                          最近更新 更多