我在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()
但我不喜欢在声明对象时要为每个属性指定一堆默认空值,因为它们稍后会被设置为实际值。我可以告诉界面默认我不提供给null的属性吗?什么让我这样做:
let x: IX = {
a: 'abc'
}
没有收到编译器错误。现在它告诉我
TS2322:类型“{}”不能分配给类型 “九”。类型“{}”中缺少属性“b”。
答案 0 :(得分:62)
我可以告诉界面默认我不提供给null的属性吗?什么会让我这样做
没有。但默认情况下它们是undefined
,这大部分都很好。您可以使用以下模式,即在创建时具有类型断言:
let x: IX = {} as any;
x.a = 'xyz'
x.b = 123
x.c = new AnotherType()
我在此处记录了这个和其他模式:https://basarat.gitbooks.io/typescript/content/docs/tips/lazyObjectLiteralInitialization.html
答案 1 :(得分:43)
您无法在界面中设置默认值,但可以使用“可选属性”(比较第3段)完成您要执行的操作:
https://www.typescriptlang.org/docs/handbook/interfaces.html
只需将界面更改为:
interface IX {
a: string,
b?: any,
c?: AnotherType
}
然后你可以这样做:
let x: IX = {
a: 'abc'
}
如果未设置这些属性,则使用init函数将默认值分配给x.b
和x.c
。
答案 2 :(得分:15)
您可以使用类实现接口,然后您可以在构造函数中处理初始化成员:
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(...);
答案 3 :(得分:4)
虽然@Timar的答案对于null
默认值(要求的)非常有效,但是这里还有一个简单的解决方案,它允许其他默认值:定义一个选项接口以及一个包含默认值的常量。在构造函数中使用spread operator来设置options
成员变量
interface IXOptions {
a?: string,
b?: any,
c?: number
}
const XDefaults: IXOptions = {
a: "default",
b: null,
c: 1
}
export class ClassX {
private options: IXOptions;
constructor(XOptions: IXOptions) {
this.options = { ...XDefaults, ...XOptions };
}
public printOptions(): void {
console.log(this.options.a);
console.log(this.options.b);
console.log(this.options.c);
}
}
现在您可以使用这样的类:
const x = new ClassX({ a: "set" });
x.printOptions();
输出:
set
null
1
答案 4 :(得分:1)
您可以为此使用工厂方法,该方法返回一个实现XI接口的对象。
class AnotherType {}
interface IX {
a: string,
b: any,
c: AnotherType | null
}
function makeIX (): IX {
return {
a: 'abc',
b: null,
c: null
}
}
const x = makeIX();
x.a = 'xyz';
x.b = 123;
x.c = new AnotherType();
我对您的示例所做的唯一更改是使属性c均为AnotherType | null
。对于没有任何编译器错误,这将是必需的(在示例中,如果将null初始化为属性c,也会出现此错误)。
答案 5 :(得分:1)
我使用以下模式:
创建实用程序类型 O(1)
:
Defaults<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);
}
}
答案 6 :(得分:0)
我偶然发现了这一点,同时寻找了一种比我所达到的更好的方法。阅读答案并进行尝试后,我认为值得一提我正在做的事情,因为其他答案对我而言并不那么简洁。对我而言,每次设置新接口时只需编写少量代码就很重要。我选择了...
使用自定义的通用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,
};
然后像这样初始化:
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
,以在分配之前迭代并检查类型。答案 7 :(得分:0)
您可以按照文档中的说明使用Partial
映射类型:
https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-1.html
在您的示例中,您将拥有:
interface IX {
a: string;
b: any;
c: AnotherType;
}
let x: Partial<IX> = {
a: 'abc'
}
答案 8 :(得分:0)
我的解决方案:
我已经在Object.assign上创建了包装器,以解决输入问题。
export function assign<T>(...args: T[] | Partial<T>[]): T {
return Object.assign.apply(Object, [{}, ...args]);
}
用法:
env.base.ts
export interface EnvironmentValues {
export interface EnvironmentValues {
isBrowser: boolean;
apiURL: string;
}
export const enviromentBaseValues: Partial<EnvironmentValues> = {
isBrowser: typeof window !== 'undefined',
};
export default enviromentBaseValues;
env.dev.ts
import { EnvironmentValues, enviromentBaseValues } from './env.base';
import { assign } from '../utilities';
export const enviromentDevValues: EnvironmentValues = assign<EnvironmentValues>(
{
apiURL: '/api',
},
enviromentBaseValues
);
export default enviromentDevValues;
答案 9 :(得分:0)
您可以使用两个单独的配置。一个作为带有可选属性(将具有默认值)的输入,另一个作为仅带有必需属性的输入。使用&
和Required
可以很方便:
interface DefaultedFuncConfig {
b?: boolean;
}
interface MandatoryFuncConfig {
a: boolean;
}
export type FuncConfig = MandatoryFuncConfig & DefaultedFuncConfig;
export const func = (config: FuncConfig): Required<FuncConfig> => ({
b: true,
...config
});
// will compile
func({ a: true });
func({ a: true, b: true });
// will error
func({ b: true });
func({});
答案 10 :(得分:0)
取决于情况和用法。通常,在TypeScript中,没有接口的默认值。
如果不使用默认值
您可以将x
声明为:
let x: IX | undefined; // declaration: x = undefined
然后,您可以在init函数中设置实数值:
x = {
a: 'xyz'
b: 123
c: new AnotherType()
};
通过这种方式,x
可以未定义或定义-undefined
表示对象是未初始化的,无需设置默认值(如果不需要)。从逻辑上讲,这比定义“垃圾”要好。
如果要部分分配对象:
您可以使用可选属性来定义类型,例如:
interface IX {
a: string,
b?: any,
c?: AnotherType
}
在这种情况下,您只需要设置a
。其他类型标有?
,表示它们是可选的,并且默认值为undefined
。
在任何情况下,您都可以使用undefined
作为默认值,这仅取决于您的用例。
答案 11 :(得分:-1)
如果您有很多参数,最好让用户只插入几个参数,而不是按特定顺序插入。
例如,不好的做法:
foo(a?, b=1, c=99, d=88, e?)
foo(null, null, null, 3)
因为您必须在实际需要的参数之前提供所有参数 (d)。
好的做法是:
foo({d=3})
实现方式是通过接口。 您需要将参数定义为如下接口:
interface Arguments {
a?;
b?;
c?;
d?;
e?;
}
并定义如下函数:
foo(arguments: Arguments)
现在接口变量不能获取默认值,那么我们如何定义默认值?
简单,我们为整个界面定义默认值:
foo({
a,
b=1,
c=99,
d=88,
e
}: Arguments)
现在如果用户通过:
foo({d=3})
实际参数为:
{
a,
b=1,
c=99,
d=3,
e
}
另一个不声明接口的选项是:
foo({
a=undefined,
b=1,
c=99,
d=88,
e=undefined
})
我从以下链接中理解了这一点,功劳很大:) https://medium.com/better-programming/named-parameters-in-typescript-e32c763d2b2e