解决TypeScript TS2322:AWSTags无法赋值给索引签名类型问题
问题背景
定义了如下AWSTags接口和返回该类型的getAWSTags函数:
export interface AWSTags { CreatedBy: string; Environment: EnvironmentMap; // EnvironmentMap为三选一的字符串字面量类型 Name: string; OwnedBy: string; Platform: string; Product: string; Runbook: string; Service: string; } export const getAWSTags = (stackName: string, environment: EnvironmentMap, createdBy: string): AWSTags => ({ CreatedBy: createdBy, Environment: environment, Name: stackName, OwnedBy: "owner", Platform: "platform", Product: "product", Runbook: "url", Service: `${stackName}-service`, });
当把getAWSTags的返回值赋值给AWS Stack的tags属性(类型为readonly tags?: {[key: string]: string;})时,触发TS2322错误:
Type 'AWSTags' is not assignable to type '{ [key: string]: string; }'. Index signature for type 'string' is missing in type 'AWSTags'.ts(2322)
下面是几种可行的解决办法:
方法1:给AWSTags添加索引签名
这是最直接的方案,因为AWSTags的所有属性值都是字符串类型(EnvironmentMap是字符串字面量,属于string的子类型),添加索引签名完全合理,既能保留明确的属性约束,又能兼容目标类型:
export interface AWSTags { [key: string]: string; // 添加索引签名 CreatedBy: string; Environment: EnvironmentMap; Name: string; OwnedBy: string; Platform: string; Product: string; Runbook: string; Service: string; }
方法2:赋值时使用类型断言
如果不想修改AWSTags接口,可在赋值时直接断言为目标类型。前提是你能确保AWSTags的所有属性值确实都是字符串类型:
const stack = new Stack(app, 'MyStack', { tags: getAWSTags('MyStack', 'prod', 'john') as {[key: string]: string} });
或者更简洁地用TS内置类型Record<string, string>(等价于{[key: string]: string}):
tags: getAWSTags('MyStack', 'prod', 'john') as Record<string, string>
方法3:修改getAWSTags的返回类型
让函数直接返回目标类型,同时内部用AWSTags约束返回结构,兼顾类型检查和兼容性:
export const getAWSTags = (stackName: string, environment: EnvironmentMap, createdBy: string): Record<string, string> => { const tags: AWSTags = { CreatedBy: createdBy, Environment: environment, Name: stackName, OwnedBy: "owner", Platform: "platform", Product: "product", Runbook: "url", Service: `${stackName}-service`, }; return tags; };
这样调用函数后直接赋值给tags属性就不会报错了。
方法4:用交叉类型合并约束和索引签名
通过交叉类型,让AWSTags同时拥有明确的属性定义和索引签名,这种方式既保留了属性的强类型检查,又满足目标类型的要求:
type AWSTags = Record<string, string> & { CreatedBy: string; Environment: EnvironmentMap; Name: string; OwnedBy: string; Platform: string; Product: string; Runbook: string; Service: string; };
这种写法和方法1效果类似,但用类型别名替代了接口,适合不需要接口扩展的场景。
内容的提问来源于stack exchange,提问作者Scarecrowsama

