使用tsyringe时未传可选构造参数报错的原因及解决方法
问题场景
我正在使用tsyringe管理依赖注入,以下代码在传入sheepProps时可正常运行:
import 'reflect-metadata' import {autoInjectable,singleton} from 'tsyringe' interface sheepProps { legCount: number } @singleton() export class Animal { makeSound (sound: string) { console.log(sound) } } @autoInjectable() class Sheep { constructor(sheepProps?: sheepProps, private animal?:Animal) {} beeeeh () { this.animal.makeSound('bleeeeh') } } const props: sheepProps = { legCount: 4 } const sheep = new Sheep(props); sheep.beeeeh();
但将构造调用改为new Sheep()(不传入sheepProps)时,抛出错误:
Error: Cannot inject the dependency "sheepProps" at position #0 of
"Sheep" constructor. Reason:
TypeInfo not known for "Object"
我的推测是:未传入sheepProps时tsyringe尝试对其注入,这个假设是否正确?如何修复以支持构造函数可选参数?
解答
你的推测完全正确
@autoInjectable()装饰器的逻辑是:当调用new Class()时,会自动为构造函数中未传入的参数尝试依赖注入。你的sheepProps是第一个参数,不传它时,tsyringe会尝试注入这个参数,但sheepProps是TypeScript接口(编译后不存在对应的JS类型,反射无法获取其类型信息),所以tsyringe不知道该注入什么,因此抛出错误。
修复方案
方案1:调整参数顺序(最简单)
将需要依赖注入的参数(比如Animal)放在构造函数参数的前面,不需要注入的可选配置参数放在最后。这样tsyringe会优先注入前面的依赖,后面的可选参数如果不传则不会被尝试注入:
@autoInjectable() class Sheep { // 交换参数顺序,Animal在前,sheepProps在后 constructor(private animal?: Animal, sheepProps?: sheepProps) {} beeeeh () { this.animal.makeSound('bleeeeh') } }
此时不管是new Sheep(props)还是new Sheep()都能正常运行:
- 传
props时,Animal由tsyringe注入,sheepProps使用传入的值 - 不传
props时,Animal正常注入,sheepProps为undefined
方案2:使用@inject(undefined)标记无需注入的参数
如果需要保留原参数顺序,可以用@inject(undefined)显式告诉tsyringe某个参数不需要依赖注入,直接使用传入值(没传则为undefined):
import { autoInjectable, singleton, inject } from 'tsyringe' // ...其他代码不变 @autoInjectable() class Sheep { constructor( @inject(undefined) sheepProps?: sheepProps, private animal?: Animal ) {} beeeeh () { this.animal.makeSound('bleeeeh') } }
这种方式适合必须保留参数顺序的场景,明确标记哪些参数是手动传入的,哪些是由DI注入的。
方案3:为参数注册可注入的默认值(仅适用于类类型)
如果sheepProps不是接口而是一个类,可以提前在tsyringe容器中注册它的默认实例,这样不传参数时tsyringe会自动注入默认值:
import 'reflect-metadata' import {autoInjectable,singleton, container} from 'tsyringe' // 将sheepProps改为类 class SheepProps { legCount: number = 4; } // 注册默认实例 container.register(SheepProps, { useValue: new SheepProps() }); @singleton() export class Animal { makeSound (sound: string) { console.log(sound) } } @autoInjectable() class Sheep { constructor(sheepProps?: SheepProps, private animal?:Animal) {} beeeeh () { this.animal.makeSound('bleeeeh') } } // 不传参数时,sheepProps会使用注册的默认实例 const sheep = new Sheep(); sheep.beeeeh();
注意:此方法仅适用于类类型,因为TypeScript接口在编译后会被移除,无法被tsyringe反射识别。
内容的提问来源于stack exchange,提问作者SecretIndividual

