强制分配undefined并启用严格空检查?

时间:2017-11-22 09:19:45

标签: typescript

我正在研究为我一直在研究的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 operatoritem.id!)。

到目前为止我能够提出的最不好的解决方案是将我传递给addItem的参数转换为any,但这会禁用对{{1}的其他属性的检查所以这不是一个很好的解决方案。我希望的是对非空断言运算符的一种模拟,它允许我断言空赋值是正常的,即使类型声明表明它不是:BusinessObject之类的东西。

我也欢迎更好/更一致的方式来描述这种模式。

3 个答案:

答案 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));
}

(为addItemgetItem添加了虚拟正文代码以保持编译器满意,只需忽略它。)

答案 2 :(得分:0)

我刚才意识到有一个运算符或多或少地按照我的描述运行:

store.addItem({ id: undefined as any, name: "one", requiredValue: 1 });

我仍然喜欢更好的长期解决方案,比如提香的建议,但这至少是一种选择。