我正在研究为我一直在研究的TypeScript项目启用strictNullChecks
标志,但它会产生很多的编译器错误。其中一些是我真正需要检查的边缘情况,但其中很多都属于特定模式。
type BusinessObject = {
id: string;
name: string;
requiredValue: number;
// ...
}
class DataStore {
// ...
public addItem(item: BusinessObject): Promise<string>;
public getItem(id: string): Promise<BusinessObject>;
}
function consumeData(store: DataStore){
store.addItem({name:"one", requiredValue:1});
store.getItem("two").then(item => console.log(item.id));
}
问题是,id
属性是由后备数据库创建的,因此在创建新项目时它只是“可选”。如果我要求id
属性(no ?:
),则在调用addItem
时会出现编译器错误。但是,如果我将ID属性设置为可选,那么对它的每个引用都必须包含一个警卫或non-null assertion operator(item.id!
)。
到目前为止我能够提出的最不好的解决方案是将我传递给addItem
的参数转换为any
,但这会禁用对{{1}的其他属性的检查所以这不是一个很好的解决方案。我希望的是对非空断言运算符的一种模拟,它允许我断言空赋值是正常的,即使类型声明表明它不是:BusinessObject
之类的东西。
我也欢迎更好/更一致的方式来描述这种模式。
答案 0 :(得分:1)
您可以有两种类型,一种在创建时表示对象,另一种表示已保存的对象
type NewBusinessObject = {
id?: string;
name: string;
requiredValue: number;
// ...
}
type BusinessObject = NewBusinessObject & {
id: string
}
declare class DataStore {
// ...
public addItem(item: NewBusinessObject): Promise<string>;
public getItem(id: string): Promise<BusinessObject>;
}
function consumeData(store: DataStore) {
store.addItem({ name: "one", requiredValue: 1 });
store.getItem("two").then(item => console.log(item.id));
}
答案 1 :(得分:1)
避免错误的一种简单方法是将addItem
的定义更改为:
public addItem(item: Partial<BusinessObject>): Promise<string>
代码现在可以工作,虽然它使BusinessObject的所有属性都可选,而不仅仅是id
。如果您对所有其他属性都有合理的默认值,那么这可能不是问题,但是否则您必须依靠创建具有和不包含id
的两种类型。
你真正想要的当然是:
public addItem(item: Omit<BusinessObject, "id">): Promise<string>
在打字稿中有类似内容的提案(例如,请参阅https://github.com/Microsoft/TypeScript/issues/12215),因此希望您的代码将来能够简化。
在这个问题上有很多,所以为了节省你一直阅读的内容,这里似乎有用(参见问题19569):
export type Diff<T extends string, U extends string> = ({[P in T]: P} &
{[P in U]: never} & {[x: string]: never})[T];
export type Omit<T, K extends keyof T> = Pick<T, Diff<keyof T, K>>;
type BusinessObject = {
id: string;
name: string;
requiredValue: number;
// ...
}
class DataStore {
// ...
public addItem(item: Omit<BusinessObject, "id">): Promise<string> {
return Promise.resolve('foo')};
public getItem(id: string): Promise<BusinessObject> {
return Promise.resolve({} as any as BusinessObject);
};
}
function consumeData(store: DataStore){
store.addItem({name:"one", requiredValue:1});
store.getItem("two").then(item => console.log(item.id));
}
(为addItem
和getItem
添加了虚拟正文代码以保持编译器满意,只需忽略它。)
答案 2 :(得分:0)
我刚才意识到有一个运算符或多或少地按照我的描述运行:
store.addItem({ id: undefined as any, name: "one", requiredValue: 1 });
我仍然喜欢更好的长期解决方案,比如提香的建议,但这至少是一种选择。