feat(core): 启用 TypeScript 最严格的类型检查 (#199)
* feat(core): 启用 TypeScript 最严格的类型检查 * ci: 配置 Codecov 以适应类型安全改进 * fix(core): 修复 CodeQL 安全警告 * fix(core): eslint.config.mjs
This commit is contained in:
@@ -7,20 +7,14 @@
|
||||
import type { ServiceContainer } from '../ServiceContainer';
|
||||
import type { IService, ServiceType } from '../ServiceContainer';
|
||||
|
||||
/**
|
||||
* 依赖注入元数据键
|
||||
*/
|
||||
const INJECTABLE_METADATA_KEY = Symbol('injectable');
|
||||
const INJECT_METADATA_KEY = Symbol('inject');
|
||||
const INJECT_PARAMS_METADATA_KEY = Symbol('inject:params');
|
||||
const UPDATABLE_METADATA_KEY = Symbol('updatable');
|
||||
|
||||
/**
|
||||
* 依赖注入元数据存储
|
||||
*/
|
||||
const injectableMetadata = new WeakMap<any, InjectableMetadata>();
|
||||
const injectMetadata = new WeakMap<any, Map<number, ServiceType<any> | string | symbol>>();
|
||||
const updatableMetadata = new WeakMap<any, UpdatableMetadata>();
|
||||
type Constructor = abstract new (...args: unknown[]) => unknown;
|
||||
|
||||
const injectableMetadata = new WeakMap<Constructor, InjectableMetadata>();
|
||||
const injectMetadata = new WeakMap<Constructor, Map<number, ServiceType<IService> | string | symbol>>();
|
||||
const updatableMetadata = new WeakMap<Constructor, UpdatableMetadata>();
|
||||
|
||||
/**
|
||||
* 可注入元数据接口
|
||||
@@ -34,13 +28,13 @@ export interface InjectableMetadata {
|
||||
/**
|
||||
* 依赖列表
|
||||
*/
|
||||
dependencies: Array<ServiceType<any> | string | symbol>;
|
||||
dependencies: Array<ServiceType<IService> | string | symbol>;
|
||||
|
||||
/**
|
||||
* 属性注入映射
|
||||
* key: 属性名, value: 服务类型
|
||||
*/
|
||||
properties?: Map<string | symbol, ServiceType<any>>;
|
||||
properties?: Map<string | symbol, ServiceType<IService>>;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -82,17 +76,15 @@ export interface UpdatableMetadata {
|
||||
* ```
|
||||
*/
|
||||
export function Injectable(): ClassDecorator {
|
||||
return function <T extends Function>(target: T): T {
|
||||
const existing = injectableMetadata.get(target);
|
||||
return function (target: Function): void {
|
||||
const existing = injectableMetadata.get(target as Constructor);
|
||||
|
||||
injectableMetadata.set(target, {
|
||||
injectableMetadata.set(target as Constructor, {
|
||||
injectable: true,
|
||||
dependencies: [],
|
||||
properties: existing?.properties
|
||||
...(existing?.properties && { properties: existing.properties })
|
||||
});
|
||||
|
||||
return target;
|
||||
};
|
||||
} as ClassDecorator;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -125,9 +117,9 @@ export function Injectable(): ClassDecorator {
|
||||
* ```
|
||||
*/
|
||||
export function Updatable(priority: number = 0): ClassDecorator {
|
||||
return function <T extends Function>(target: T): T {
|
||||
return function (target: Function): void {
|
||||
// 验证类原型上是否有update方法
|
||||
const prototype = (target as any).prototype;
|
||||
const prototype = (target as Constructor & { prototype: { update?: unknown } }).prototype;
|
||||
if (!prototype || typeof prototype.update !== 'function') {
|
||||
throw new Error(
|
||||
`@Updatable() decorator requires class ${target.name} to implement IUpdatable interface with update() method. ` +
|
||||
@@ -136,13 +128,11 @@ export function Updatable(priority: number = 0): ClassDecorator {
|
||||
}
|
||||
|
||||
// 标记为可更新
|
||||
updatableMetadata.set(target, {
|
||||
updatableMetadata.set(target as Constructor, {
|
||||
updatable: true,
|
||||
priority
|
||||
});
|
||||
|
||||
return target;
|
||||
};
|
||||
} as ClassDecorator;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -152,13 +142,13 @@ export function Updatable(priority: number = 0): ClassDecorator {
|
||||
*
|
||||
* @param serviceType 服务类型标识符
|
||||
*/
|
||||
export function Inject(serviceType: ServiceType<any> | string | symbol): ParameterDecorator {
|
||||
return function (target: Object, propertyKey: string | symbol | undefined, parameterIndex: number) {
|
||||
export function Inject(serviceType: ServiceType<IService> | string | symbol): ParameterDecorator {
|
||||
return function (target: object, _propertyKey: string | symbol | undefined, parameterIndex: number) {
|
||||
// 获取或创建注入元数据
|
||||
let params = injectMetadata.get(target);
|
||||
let params = injectMetadata.get(target as Constructor);
|
||||
if (!params) {
|
||||
params = new Map();
|
||||
injectMetadata.set(target, params);
|
||||
injectMetadata.set(target as Constructor, params);
|
||||
}
|
||||
|
||||
// 记录参数索引和服务类型的映射
|
||||
@@ -175,16 +165,16 @@ export function Inject(serviceType: ServiceType<any> | string | symbol): Paramet
|
||||
*
|
||||
* @param serviceType 服务类型
|
||||
*/
|
||||
export function InjectProperty(serviceType: ServiceType<any>): PropertyDecorator {
|
||||
return function (target: any, propertyKey: string | symbol) {
|
||||
let metadata = injectableMetadata.get(target.constructor);
|
||||
export function InjectProperty(serviceType: ServiceType<IService>): PropertyDecorator {
|
||||
return function (target: object, propertyKey: string | symbol) {
|
||||
let metadata = injectableMetadata.get((target as { constructor: Constructor }).constructor);
|
||||
|
||||
if (!metadata) {
|
||||
metadata = {
|
||||
injectable: true,
|
||||
dependencies: []
|
||||
};
|
||||
injectableMetadata.set(target.constructor, metadata);
|
||||
injectableMetadata.set((target as { constructor: Constructor }).constructor, metadata);
|
||||
}
|
||||
|
||||
if (!metadata.properties) {
|
||||
@@ -201,7 +191,7 @@ export function InjectProperty(serviceType: ServiceType<any>): PropertyDecorator
|
||||
* @param target 目标类
|
||||
* @returns 是否可注入
|
||||
*/
|
||||
export function isInjectable(target: any): boolean {
|
||||
export function isInjectable(target: Constructor): boolean {
|
||||
const metadata = injectableMetadata.get(target);
|
||||
return metadata?.injectable ?? false;
|
||||
}
|
||||
@@ -212,7 +202,7 @@ export function isInjectable(target: any): boolean {
|
||||
* @param target 目标类
|
||||
* @returns 依赖注入元数据
|
||||
*/
|
||||
export function getInjectableMetadata(target: any): InjectableMetadata | undefined {
|
||||
export function getInjectableMetadata(target: Constructor): InjectableMetadata | undefined {
|
||||
return injectableMetadata.get(target);
|
||||
}
|
||||
|
||||
@@ -222,7 +212,7 @@ export function getInjectableMetadata(target: any): InjectableMetadata | undefin
|
||||
* @param target 目标类
|
||||
* @returns 参数索引到服务类型的映射
|
||||
*/
|
||||
export function getInjectMetadata(target: any): Map<number, ServiceType<any> | string | symbol> {
|
||||
export function getInjectMetadata(target: Constructor): Map<number, ServiceType<IService> | string | symbol> {
|
||||
return injectMetadata.get(target) || new Map();
|
||||
}
|
||||
|
||||
@@ -243,10 +233,10 @@ export function createInstance<T>(
|
||||
container: ServiceContainer
|
||||
): T {
|
||||
// 获取参数注入元数据
|
||||
const injectParams = getInjectMetadata(constructor);
|
||||
const injectParams = getInjectMetadata(constructor as Constructor);
|
||||
|
||||
// 解析依赖
|
||||
const dependencies: any[] = [];
|
||||
const dependencies: unknown[] = [];
|
||||
|
||||
// 获取构造函数参数数量
|
||||
const paramCount = constructor.length;
|
||||
@@ -264,7 +254,7 @@ export function createInstance<T>(
|
||||
);
|
||||
} else {
|
||||
// 类类型
|
||||
dependencies.push(container.resolve(serviceType as ServiceType<any>));
|
||||
dependencies.push(container.resolve(serviceType as ServiceType<IService>));
|
||||
}
|
||||
} else {
|
||||
// 没有@Inject标记,传入undefined
|
||||
@@ -282,8 +272,8 @@ export function createInstance<T>(
|
||||
* @param instance 目标实例
|
||||
* @param container 服务容器
|
||||
*/
|
||||
export function injectProperties<T>(instance: T, container: ServiceContainer): void {
|
||||
const constructor = (instance as any).constructor;
|
||||
export function injectProperties<T extends object>(instance: T, container: ServiceContainer): void {
|
||||
const constructor = (instance as { constructor: Constructor }).constructor;
|
||||
const metadata = getInjectableMetadata(constructor);
|
||||
|
||||
if (!metadata?.properties || metadata.properties.size === 0) {
|
||||
@@ -294,7 +284,7 @@ export function injectProperties<T>(instance: T, container: ServiceContainer): v
|
||||
const service = container.resolve(serviceType);
|
||||
|
||||
if (service !== null) {
|
||||
(instance as any)[propertyKey] = service;
|
||||
(instance as Record<string | symbol, unknown>)[propertyKey] = service;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -305,7 +295,7 @@ export function injectProperties<T>(instance: T, container: ServiceContainer): v
|
||||
* @param target 目标类
|
||||
* @returns 是否可更新
|
||||
*/
|
||||
export function isUpdatable(target: any): boolean {
|
||||
export function isUpdatable(target: Constructor): boolean {
|
||||
const metadata = updatableMetadata.get(target);
|
||||
return metadata?.updatable ?? false;
|
||||
}
|
||||
@@ -316,7 +306,7 @@ export function isUpdatable(target: any): boolean {
|
||||
* @param target 目标类
|
||||
* @returns 可更新元数据
|
||||
*/
|
||||
export function getUpdatableMetadata(target: any): UpdatableMetadata | undefined {
|
||||
export function getUpdatableMetadata(target: Constructor): UpdatableMetadata | undefined {
|
||||
return updatableMetadata.get(target);
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ export interface IService {
|
||||
* 服务类型
|
||||
*
|
||||
* 支持任意构造函数签名,以便与依赖注入装饰器配合使用
|
||||
* 使用 any[] 以允许任意参数类型的构造函数
|
||||
*/
|
||||
export type ServiceType<T extends IService> = new (...args: any[]) => T;
|
||||
|
||||
@@ -108,7 +109,7 @@ export class ServiceContainer {
|
||||
* 自动收集所有使用@Updatable装饰器标记的服务,供Core统一更新
|
||||
* 按优先级排序(数值越小越先执行)
|
||||
*/
|
||||
private _updatableServices: Array<{ instance: any; priority: number }> = [];
|
||||
private _updatableServices: Array<{ instance: IService; priority: number }> = [];
|
||||
|
||||
/**
|
||||
* 注册单例服务
|
||||
@@ -138,7 +139,7 @@ export class ServiceContainer {
|
||||
|
||||
this._services.set(type as ServiceType<IService>, {
|
||||
type: type as ServiceType<IService>,
|
||||
factory: factory as ((container: ServiceContainer) => IService) | undefined,
|
||||
...(factory && { factory: factory as (container: ServiceContainer) => IService }),
|
||||
lifetime: ServiceLifetime.Singleton
|
||||
});
|
||||
|
||||
@@ -170,7 +171,7 @@ export class ServiceContainer {
|
||||
|
||||
this._services.set(type as ServiceType<IService>, {
|
||||
type: type as ServiceType<IService>,
|
||||
factory: factory as ((container: ServiceContainer) => IService) | undefined,
|
||||
...(factory && { factory: factory as (container: ServiceContainer) => IService }),
|
||||
lifetime: ServiceLifetime.Transient
|
||||
});
|
||||
|
||||
@@ -191,7 +192,7 @@ export class ServiceContainer {
|
||||
* container.registerInstance(Config, config);
|
||||
* ```
|
||||
*/
|
||||
public registerInstance<T extends IService>(type: new (...args: any[]) => T, instance: T): void {
|
||||
public registerInstance<T extends IService>(type: ServiceType<T>, instance: T): void {
|
||||
if (this._services.has(type as ServiceType<IService>)) {
|
||||
logger.warn(`Service ${type.name} is already registered`);
|
||||
return;
|
||||
@@ -391,7 +392,7 @@ export class ServiceContainer {
|
||||
*/
|
||||
public updateAll(deltaTime?: number): void {
|
||||
for (const { instance } of this._updatableServices) {
|
||||
instance.update(deltaTime);
|
||||
(instance as IService & { update: (deltaTime?: number) => void }).update(deltaTime);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user