如何告诉 TypeScript 一个对象的函数将填充一个否则为 null 的属性?

huangapple go评论207阅读模式
英文:

How can I tell typescript that an object's function will populate an otherwise null property?

问题

我的问题与这个类似:

Type Name = {firstName: string, lastName: string}

class NameClass {
  public _name: Name | null;

  constructor() {
    this._name = null;
  }

  // 或任何函数
  public set name(name: Name) {
    this._name = name;
  }

  public printName() {
    if (!this._name) this.name = { firstName: "should not", lastName: "be null" };
    //          v 即使我刚刚设置了,对象可能为空 ^
    console.log(this._name.firstName);
  }
}

我知道可以简单地使用 "!" 断言来解决这个问题,或者可以创建一个单独的函数来确保对象不为空,但我正在寻找一种告诉 TypeScript 函数将设置属性的方法。

问题中提出了一个解决方案,但我认为这种方法有点巧妙,而且并没有真正解决问题。这个帖子也是4年前的了,所以我希望有更好的方法来解决这个问题。

英文:

My problem is similar to this:

Type Name = {firstName: string, lastName: string}

class NameClass {
  public _name: Name | null;

  constructor() {
    this._name = null;
  }

  // or any function
  public set name(name: Name) {
    this._name = name;
  }

  public printName() {
    if (!this._name) this.name = { firstName: "should not", lastName: "be null" };
    //          v Object may be null even though I just set it ^
    console.log(this._name.firstName);
  }
}

I'm aware I can simply use the " ! " assertion to get around this or can create a separate function to make sure that the object is not null, but I'm looking for a way to tell typescript that the function will set the property.

There is a solution proposed in question

but I find this kind of hacky and doesn't really solve the problem. The post was also 4 years ago, so I'm hoping there is a better way to fix this?

答案1

得分: 1

你考虑过在构造函数中初始化 _name 属性吗?

type Name = {firstName: string, lastName: string}

class NameClass {
  public _name: Name;

  constructor() {
    this._name = { firstName: "不应该", lastName: "为 null" };
  }

  // 或者任何函数
  public set name(name: Name) {
    this._name = name;
  }

  public printName() {
    console.log(this._name.firstName);
  }
}

TypeScript 允许这样做,因为这样 _name 总是被设置。

英文:

Have you considered initializing the _name property in the constructor?

type Name = {firstName: string, lastName: string}

class NameClass {
  public _name: Name;

  constructor() {
    this._name = { firstName: "should not", lastName: "be null" };
  }

  // or any function
  public set name(name: Name) {
    this._name = name;
  }

  public printName() {
    console.log(this._name.firstName);
  }
}

Typescript is okay with that, because that way _name is always set.

答案2

得分: 0

如果我没有错,问题并不是告诉 TypeScript "如何做" - 这根本不可能。这个类不能也永远不会知道,在哪个函数中(或其他方式)设置了可空属性是什么。拥有可空属性的整个目的就是有机会将该属性设置为 null。即使在设置属性后,它也可能返回到 null(例如通过另一个函数)。

一个解决方法是在构造函数中将该属性设置为默认字符串值("")。

另外作为一点注意,不建议使用下划线开头的公共属性。通常,只有private字段会这样命名。

英文:

If im not mistaken, the problem is not telling typescript "how to do it" - it is simply not possible. The class cannot and will never know, what nullable property was set in what function (or some other way). The whole point of having a nullable property is to have the opportunity to set that property to null. Even after setting the property, it may return back to null (e. g. through another function).

A work around would be having that property set to defalt string values ("") in the constructor.

Also as a side-note, it is not recommendet to have a public property start with an underscore. Normally, only private fields are named like that.

答案3

得分: 0

这个版本进行类型检查:

type Name = { firstName: string; lastName: string };

class NameClass {
  public _name: Name | null;

  constructor() {
    this._name = null;
  }

  ensureName(): asserts this is { _name: Name } {
    if (!this._name)
      this.name = { firstName: 'should not', lastName: 'be null' };
  }

  // 或者任何函数
  public set name(name: Name) {
    this._name = name;
  }

  public printName() {
    this.ensureName();
    
    console.log(this._name.firstName);
  }
}

但我认为使用非空断言运算符更符合习惯,并且在大多数情况下,TypeScript 相信您使用 asserts ... 语法时提供足够的保证。

英文:

This version type checks:

type Name = { firstName: string; lastName: string };

class NameClass {
  public _name: Name | null;

  constructor() {
    this._name = null;
  }

  ensureName(): asserts this is { _name: Name } {
    if (!this._name)
      this.name = { firstName: 'should not', lastName: 'be null' };
  }

  // or any function
  public set name(name: Name) {
    this._name = name;
  }

  public printName() {
    this.ensureName();
    
    console.log(this._name.firstName);
  }
}

but I think using the non-null assertion operator is more idiomatic, and doesn't give you less guarantees, as TS mostly trusts you regarding the asserts ... syntax.

huangapple
  • 本文由 发表于 2023年8月10日 20:33:30
  • 转载请务必保留本文链接:https://go.coder-hub.com/76875779.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定