如何基于异步函数设置类属性?TypeScript类型推断疑问
解决方案
1. 静态异步工厂方法(推荐)
直接用静态异步方法创建并初始化实例,外部无需手动调用init,拿到的实例天然保证itemCategories已经赋值,TypeScript能直接推断出属性非空。
修改后的代码:
import type { Application } from '../application'; export class ShopManager { private readonly _application: Application; private _itemCategories: string[]; // 无需再声明|null public get itemCategories() { return this._itemCategories; } // 构造函数改为私有,强制通过工厂方法创建实例 private constructor(application: Application) { this._application = application; } // 静态异步工厂方法,负责初始化并返回实例 public static async create(application: Application): Promise<ShopManager> { const manager = new ShopManager(application); manager._itemCategories = await manager._getItemCategories(); return manager; } private async _getItemCategories(): Promise<string[]> { const categories = await this._application.storage.itemCategory.findMany(); return categories.map((category) => category.name); } }
使用方式:
// 直接获取已初始化的实例 const shopManager = await ShopManager.create(application); // 此时itemCategories是确定的string[],无需空检查 console.log(shopManager.itemCategories.length);
这个方案从根源上避免了未初始化状态,外部调用更简洁,类型也更安全。
2. 类型守卫/断言函数(兼容现有结构)
如果不想改动现有类的构造方式,可以添加类型守卫或断言函数,让TypeScript识别实例是否已初始化,从而跳过空检查。
修改后的代码:
import type { Application } from '../application'; export class ShopManager { private readonly _application: Application; private _itemCategories: string[] | null = null; public get itemCategories() { return this._itemCategories; } public constructor(application: Application) { this._application = application; } public async init() { this._itemCategories = await this._getItemCategories(); } // 类型守卫函数,判断实例是否已初始化 public isInitialized(): this is ShopManager & { itemCategories: string[] } { return this._itemCategories !== null; } // 断言函数,直接告知TypeScript实例已初始化(适合确定已调用init的场景) public assertInitialized(): asserts this is ShopManager & { itemCategories: string[] } { if (this._itemCategories === null) { throw new Error('ShopManager has not been initialized. Call init() first.'); } } private async _getItemCategories(): Promise<string[]> { const categories = await this._application.storage.itemCategory.findMany(); return categories.map((category) => category.name); } }
使用方式:
const shopManager = new ShopManager(application); await shopManager.init(); // 用类型守卫缩小类型 if (shopManager.isInitialized()) { console.log(shopManager.itemCategories.length); // 无需空检查 } // 或者用断言函数 shopManager.assertInitialized(); console.log(shopManager.itemCategories.length); // 无需空检查
这个方案适合需要保留现有类结构的场景,通过类型工具让TypeScript识别初始化状态,减少冗余的空检查。
3. 可选:使用Promise作为属性类型
如果业务允许延迟获取属性值,可以把_itemCategories设为Promise<string[]>,省去单独的init方法,属性本身就是异步的:
import type { Application } from '../application'; export class ShopManager { private readonly _application: Application; private readonly _itemCategories: Promise<string[]>; public get itemCategories() { return this._itemCategories; } public constructor(application: Application) { this._application = application; this._itemCategories = this._getItemCategories(); } private async _getItemCategories(): Promise<string[]> { const categories = await this._application.storage.itemCategory.findMany(); return categories.map((category) => category.name); } }
使用方式:
const shopManager = new ShopManager(application); // 使用时需要await const categories = await shopManager.itemCategories; console.log(categories.length);
这个方案适合不需要立即同步获取属性的场景,省去了init调用,但每次使用属性都需要await。
内容的提问来源于stack exchange,提问作者Viriato
相关产品推荐
相关产品推荐

