我在TypeScript中有以下接口:
interface IX {
a: string,
b: any,
c: AnotherType
}
我声明了一个该类型的变量并初始化了所有属性
let x: IX = {
a: 'abc',
b: null,
c: null
}
然后在稍后的init函数中为它们赋值
x.a = 'xyz'
x.b = 123
x.c = new AnotherType()
但我不喜欢在声明对象时为每个属性指定一堆默认空值,因为它们稍后将被设置为实值。我能告诉接口默认属性我不提供为空吗?是什么让我这样做:
let x: IX = {
a: 'abc'
}
而不会产生编译器错误。现在它告诉我了
TS2322:类型“{}”不能赋值给类型
“九”。属性“b”在类型“{}”中缺失。
你可以用一个类实现接口,然后你可以在构造函数中初始化成员:
class IXClass implements IX {
a: string;
b: any;
c: AnotherType;
constructor(obj: IX);
constructor(a: string, b: any, c: AnotherType);
constructor() {
if (arguments.length == 1) {
this.a = arguments[0].a;
this.b = arguments[0].b;
this.c = arguments[0].c;
} else {
this.a = arguments[0];
this.b = arguments[1];
this.c = arguments[2];
}
}
}
另一种方法是使用工厂函数:
function ixFactory(a: string, b: any, c: AnotherType): IX {
return {
a: a,
b: b,
c: c
}
}
然后你可以简单地:
var ix: IX = null;
...
ix = new IXClass(...);
// or
ix = ixFactory(...);
另一种方法是使用https://www.npmjs.com/package/merge
这和上一个答案是一样的,但是更简洁一些。
让我们安装合并
yarn add -D merge
接下来让我们创建一个带有一些选项的接口。
我们把它放到
/ / index.ts类型
export interface ExampleOpts {
opt1: string,
opt2: string,
opt3: string,
}
接下来让我们创建一组默认值
你可以把它放在同一个文件中,但让我们保持类型分离,并将其放入
/ config / index.ts
import { ExampleOpts } from '../types'
// Defaults
export const ExampleOptsDefault : ExampleOpts = {
opt1: 'default value 1',
opt2: 'default value 2',
opt3: 'default value 3',
}
接下来让我们用一个函数将它们全部连接在一起
。/ index.ts
import { ExampleOpts } from './types'
import { ExampleOptsDefault } from './config'
import merge from 'merge'
// The ? makes the parameter optional
export function test1(options?: ExampleOpts) {
// merge tries to load in the defaults first, then options next if it's defined
const merged_opts: ExampleOpts = merge.recursive(ExampleOptsDefault, options)
// log the result to the console
console.log(merged_opts)
}
我使用下面的模式:
创建工具类型默认值<T>:
type OptionalKeys<T> = { [K in keyof T]-?: {} extends Pick<T, K> ? K : never }[keyof T];
type Defaults<T> = Required<Pick<T, OptionalKeys<T>>>
用选项/默认值声明类:
// options passed to class constructor
export interface Options {
a: string,
b?: any,
c?: number
}
// defaults
const defaults: Defaults<Options> = {
b: null,
c: 1
};
export class MyClass {
// all options in class must have values
options: Required<Options>;
constructor(options: Options) {
// merge passed options and defaults
this.options = Object.assign({}, defaults, options);
}
}
创建类实例:
const myClass = new MyClass({
a: 'hello',
b: true,
});
console.log(myClass.options);
// { a: 'hello', b: true, c: 1 }
这要看情况和用法而定。通常,在TypeScript中,接口没有默认值。
如果您不使用默认值
你可以声明x为:
let x: IX | undefined; // declaration: x = undefined
然后,在你的init函数中,你可以设置实值:
x = {
a: 'xyz'
b: 123
c: new AnotherType()
};
这样,x可以是undefined或defined - undefined表示对象未初始化,如果不需要默认值,则不设置默认值。这在逻辑上比定义“垃圾”要好。
如果你想部分赋值对象:
你可以用可选属性定义类型,比如:
interface IX {
a: string,
b?: any,
c?: AnotherType
}
在这种情况下,您只需要设置a。其他类型用?这意味着它们是可选的,并具有未定义的默认值。
甚至
let x: Partial<IX> = { ... }
这使得所有字段都是可选的。
在任何情况下,你都可以使用undefined作为默认值,这只是取决于你的用例。
这是我在寻找比我已经得到的更好的方法时偶然发现的。在阅读了答案并尝试了它们之后,我认为值得把我正在做的事情发布出来,因为其他答案对我来说感觉不那么简洁。对我来说,每次设置新界面时只需要编写少量代码是很重要的。我选定了……
使用自定义通用deepCopy函数:
deepCopy = <T extends {}>(input: any): T => {
return JSON.parse(JSON.stringify(input));
};
定义接口
interface IX {
a: string;
b: any;
c: AnotherType;
}
... 并在单独的const中定义默认值。
const XDef : IX = {
a: '',
b: null,
c: null,
};
然后像这样init:
let x : IX = deepCopy(XDef);
这就是所需要的。
. .然而. .
如果你想自定义初始化任何根元素,你可以修改deepCopy函数来接受自定义默认值。函数变成:
deepCopyAssign = <T extends {}>(input: any, rootOverwrites?: any): T => {
return JSON.parse(JSON.stringify({ ...input, ...rootOverwrites }));
};
然后可以这样调用:
let x : IX = deepCopyAssign(XDef, { a:'customInitValue' } );
任何其他首选的深度复制方式都可以工作。如果只需要一个浅拷贝,那么Object。assign就足够了,不需要使用实用程序deepCopy或deepCopyAssign函数。
let x : IX = object.assign({}, XDef, { a:'customInitValue' });
已知的问题
在这种情况下,它不会深入分配,但并不太难
修改deepCopyAssign以迭代并在赋值前检查类型。
解析/stringify过程将丢失函数和引用。
我的任务不需要这些,OP也不需要。
自定义init值在执行时不会被IDE提示或类型检查。