IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    TS – Index Signatures

    zhuxy发表于 2024-06-12 16:10:56
    love 0

    你有 2 个描述,软件开发人员薪水的对象:

    const salary1 = {  
    
        baseSalary: 100_000,  
        
        yearlyBonus: 20_000  
    
    };  
    
      
    
    const salary2 = {  
    
        contractSalary: 110_000  
    
    };
    

    您想实现一个根据工资对象返回总薪酬的函数:

    function totalSalary(salaryObject: ???) {  
    
    	let total = 0;  
    	
    	for (const name in salaryObject) {  
    	
    		total += salaryObject[name];  
    	
    	}  
    	
    	return total;  
    
    }  
    
    console.log(totalSalary(salary1)); // => 120_000  
    
    console.log(totalSalary(salary2)); // => 110_000
    

    您将如何注释totalSalary()函数的salaryObject参数以接受键为字符串、值为数字的对象?

    答案是使用索引签名!

    让我们找到什么是TypeScript索引签名以及何时需要它们。

    1.为什么要索引签名

    索引签名的思想是在您只知道键和值类型时键入未知结构的对象。

    索引签名适合薪水参数的情况:该函数应该接受不同结构的薪水对象-只需确保对象值是数字。

    让我们用索引签名注释salaryObject参数:

    function totalSalary(salaryObject: { [key: string]: number }) {  
    
    	let total = 0;  
    	
    	for (const name in salaryObject) {  
    	
    		total += salaryObject[name];  
    	
    	}  
    	
    	return total;  
    
    }  
    
      
    
    console.log(totalSalary(salary1)); // => 120_000  
    
    console.log(totalSalary(salary2)); // => 110_000
    

    { [key: string]: number }是索引签名,它告诉TypeScriptsalaryObject必须是一个以stringtype作为键和numbertype作为值的对象。

    现在totalSalary()接受salary1和salary2对象作为参数,因为它们是具有数字值的对象。

    但是,该函数不会接受具有例如字符串作为值的对象:

    const salary3 = {  
    
    	baseSalary: '100 thousands'  
    
    };  
    
      
    
    // Type error:  
    
    // Argument of type '{ baseSalary: string; }' is not assignable to parameter of type '{ [key: string]: number; }'.  
    
    // Property 'baseSalary' is incompatible with index signature.  
    
    // Type 'string' is not assignable to type 'number'.  
    
    totalSalary(salary3);
    

    2.索引签名语法

    索引签名的语法很简单,看起来类似于属性的语法。但有一个区别:将键的类型写在方括号内:{ [key: KeyType]: ValueType }。

    以下是索引签名的几个示例。

    stringtype是键和值:

    interface StringByString {  
    
    	[key: string]: string;  
    
    }  
    
      
    
    const heroesInBooks: StringByString = {  
    
    	'Gunslinger': 'The Dark Tower',  
    	
    	'Jack Torrance': 'The Shining'  
    
    };
    

    string类型是键,值可以是string、number或boolean:

    interface Options {  
    
    	[key: string]: string | number | boolean;  
    	
    	timeout: number;  
    
    }  
    
      
    
    const options: Options = {  
    
    	timeout: 1000,  
    	
    	timeoutMessage: 'The request timed out!',  
    	
    	isFileUpload: false  
    
    };
    

    Options接口还有一个字段timeout,它在索引签名附近工作正常。

    索引签名的键只能是string、number或symbol。不允许其他类型:

    interface OopsDictionary {  
    
    // Type error:  
    
    // An index signature parameter type must be 'string', 'number',  
    
    // 'symbol', or a template literal type.  
    
    	[key: boolean]: string;  
    
    }
    

    3.索引签名警告

    TypeScript中的索引签名有一些您应该注意的注意事项。

    3.1不存在的财产

    如果您尝试访问索引签名为{ [key: string]: string }的对象的不存在属性会发生什么?

    正如预期的那样,TypeScript将值的类型推断为string。但是如果您检查运行时值-它是undefined:

    interface StringByString {  
    
    	[key: string]: string;  
    
    }  
    
      
    
    const object: StringByString = {};  
    
      
    
    const value = object['nonExistingProp'];  
    
    console.log(value); // => undefined
    

    value根据TypeScript,变量是string类型,但是,它的运行时值是undefined。

    索引签名将键类型映射到值类型-仅此而已。如果您不正确映射,值类型可能会偏离实际的运行时数据类型。

    为了使键入更准确,请将索引值标记为string或undefined。这样做,TypeScript会意识到您访问的属性可能不存在:

    interface StringByString {  
    
    	[key: string]: string | undefined;  
    
    }  
    
      
    
    const object: StringByString = {};  
    
      
    
    const value = object['nonExistingProp'];  
    
    console.log(value); // => undefined
    

    3.2字符串和数字键

    假设您有一本数字名称字典:

    interface NumbersNames {  
    
    	[key: string]: string  
    
    }  
    
      
    
    const names: NumbersNames = {  
    
    	'1': 'one',  
    	
    	'2': 'two',  
    	
    	'3': 'three',  
    	
    	// etc...  
    
    };
    

    通过字符串键访问值按预期工作:

    const value1 = names['1']; // OK
    

    如果您通过数字1访问值会出错吗?

    const value2 = names[1]; // OK  
    

    没有,都很好!

    当在属性访问器中用作键时,JavaScript会将数字隐式强制转换为字符串(names[1]与names['1']相同)。TypeScript也执行这种强制。

    您可以认为[key: string]与[key: string | number]相同。

    4.索引签名与记录

    TypeScript有一个实用程序类型Record<Keys, Values>来注释记录,类似于索引签名。

    const object1: Record<string, string> = { prop: 'Value' }; // OK  
    
    const object2: { [key: string]: string } = { prop: 'Value' }; // OK
    

    最大的问题是…什么时候使用Record<Keys, Values>以及什么时候使用索引签名?乍一看,它们看起来很相似!

    如您之前所见,索引签名仅接受string、number或symbol作为密钥类型。例如,如果您尝试使用字符串文字类型的并集作为索引签名中的键,则会出错:

    interface Salary {  
    
    // Type error:  
    
    // An index signature parameter type cannot be a literal type or generic type.  
    
    // Consider using a mapped object type instead.  
    
    	[key: 'yearlySalary' | 'yearlyBonus']: number  
    
    }
    

    这种行为表明_索引签名在键方面是通用的。_

    但是您可以使用字符串文字的并集来描述Record<Keys, Values>中的键:

    type SpecificSalary = Record<'yearlySalary'|'yearlyBonus', number>  
    
    type GenericSalary = Record<string, number>  
    
      
    
    const salary1: SpecificSalary = {  
    
    	'yearlySalary': 120_000,  
    	
    	'yearlyBonus': 10_000  
    
    }; // OK
    

    如果您想将键限制为特定字符串的并集,则Record<'prop1' | 'prop2' | ... | 'propN', Values>是替代索引签名的方法。

    5.结论

    当您不知道对象的确切结构,但您知道键和值类型时,索引签名注释非常适合这种情况。

    索引签名由方括号中的索引名称及其类型组成,后跟冒号和值类型:{ [indexName: Keys]: Values }。Keys可以是string、number或symbol,而Values可以是任何类型。

    要将键类型限制为特定的字符串并集,则使用Record<Keys, Values>utilty类型是一个更好的主意。索引签名不支持字符串文字类型的并集。



沪ICP备19023445号-2号
友情链接