断言接口中的可选属性在编译时 class 中不为空
Assert that optional properties in interface are not null in class at compile time
在 Javascript 中,我喜欢将 options object
传递给 constructor
,并使用默认值填写我期望的任何缺失属性:
function foo(options) {
if(options.a == undefined) {
options.a = 1;
}
if(options.b == undefined) {
options.b = 'hello';
}
this.options = options;
}
new foo({a: 10});
尝试在 Typescript 中实现此模式,我有:
interface Options {
a?: number;
b?: string;
}
class Foo {
options: Options;
constructor(options: Options) {
if(options.a == undefined) {
options.a = 1;
}
if(options.b == undefined) {
options.b = 'hello';
}
this.options = options;
}
}
当我在class
之后使用options
时,我知道所有的属性都定义好了。但是,Typescript 并不知道这一点,因此该语句会产生错误:
let a:Number = this.options.a; // Argument of type 'number | undefined' is not assignable to parameter of type number.
此外,我不想让 class
中的代码稍后将 null
或 undefined
值分配给 属性:
this.options.a = undefined; // should produce compiler error
有没有办法定义我的类型,所以 Options
的属性在 class
中使用时不能为 null,而无需定义两个 Options
接口?
例如,我知道我可以制作另一个 interface
供内部使用:
interface OptionsSafe {
a: number;
b: string;
}
// in Foo constructor
this.options = {
a: a != undefined ? options.a! : 1,
b: b != undefined ? options.b! : 'hello'
};
但是我需要在两个单独的接口中定义选项的属性,并确保它们始终同步(如果 Typescript 可以从一个定义生成两个接口,这个解决方案会很棒)。
我也不想将所有属性作为单独的可选参数传递,因为
- 可以有几十个或更多选项
- 函数参数可能会偏离
Options
界面,所以我仍然需要保持两个 "interfaces" 同步。
一种选择是使用 Typescript 提供的内置 Partial
类型。这对您来说可能太接近 "defining two Options
interfaces",但无论如何我都会告诉您我的意思。
Typescript 提供了一个内置类型 Partial
,定义为 type Partial<T> = {[P in keyof T]?: T[P]}
。
这意味着您可以只定义具有所有成员的 Options
接口,但将其部分版本用于您的构造函数。像这样:
interface Options {
a: number;
b: string;
}
class Foo {
options: Options;
constructor(options: Partial<Options>) {
const defaultOptions = {
a: 1,
b: 'hello'
};
this.options = Object.assign(defaultOptions, options);
}
}
为了让 Typescript 正确推断您已获得对象的所有必要属性,您需要从您使用的 if
语句逻辑转移到更像上面的内容。
在 Javascript 中,我喜欢将 options object
传递给 constructor
,并使用默认值填写我期望的任何缺失属性:
function foo(options) {
if(options.a == undefined) {
options.a = 1;
}
if(options.b == undefined) {
options.b = 'hello';
}
this.options = options;
}
new foo({a: 10});
尝试在 Typescript 中实现此模式,我有:
interface Options {
a?: number;
b?: string;
}
class Foo {
options: Options;
constructor(options: Options) {
if(options.a == undefined) {
options.a = 1;
}
if(options.b == undefined) {
options.b = 'hello';
}
this.options = options;
}
}
当我在class
之后使用options
时,我知道所有的属性都定义好了。但是,Typescript 并不知道这一点,因此该语句会产生错误:
let a:Number = this.options.a; // Argument of type 'number | undefined' is not assignable to parameter of type number.
此外,我不想让 class
中的代码稍后将 null
或 undefined
值分配给 属性:
this.options.a = undefined; // should produce compiler error
有没有办法定义我的类型,所以 Options
的属性在 class
中使用时不能为 null,而无需定义两个 Options
接口?
例如,我知道我可以制作另一个 interface
供内部使用:
interface OptionsSafe {
a: number;
b: string;
}
// in Foo constructor
this.options = {
a: a != undefined ? options.a! : 1,
b: b != undefined ? options.b! : 'hello'
};
但是我需要在两个单独的接口中定义选项的属性,并确保它们始终同步(如果 Typescript 可以从一个定义生成两个接口,这个解决方案会很棒)。
我也不想将所有属性作为单独的可选参数传递,因为
- 可以有几十个或更多选项
- 函数参数可能会偏离
Options
界面,所以我仍然需要保持两个 "interfaces" 同步。
一种选择是使用 Typescript 提供的内置 Partial
类型。这对您来说可能太接近 "defining two Options
interfaces",但无论如何我都会告诉您我的意思。
Typescript 提供了一个内置类型 Partial
,定义为 type Partial<T> = {[P in keyof T]?: T[P]}
。
这意味着您可以只定义具有所有成员的 Options
接口,但将其部分版本用于您的构造函数。像这样:
interface Options {
a: number;
b: string;
}
class Foo {
options: Options;
constructor(options: Partial<Options>) {
const defaultOptions = {
a: 1,
b: 'hello'
};
this.options = Object.assign(defaultOptions, options);
}
}
为了让 Typescript 正确推断您已获得对象的所有必要属性,您需要从您使用的 if
语句逻辑转移到更像上面的内容。