Typescript接口默认值

Typescript接口默认值,typescript,Typescript,我在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() 但我不喜欢在声明对象时为每个属性指定一组

我在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值,而这些属性稍后将被设置为实值。我可以告诉接口将我没有提供的属性默认为null吗?我该怎么做:

let x: IX = {
    a: 'abc'
}
没有得到编译器错误。现在它告诉我

TS2322:类型“{}”不可分配给类型 “九”。类型“{}”中缺少属性“b”

我可以告诉接口将我没有提供的属性默认为null吗?什么会让我这么做

不可以。您不能为接口或类型别名提供默认值,因为它们仅在编译时提供,并且默认值需要运行时支持

可供替代的 但在JavaScript运行时中,未指定的值默认为
未定义的
。因此,您可以将它们标记为可选:

interface IX {
  a: string,
  b?: any,
  c?: AnotherType
}
现在,当您创建它时,您只需要提供
a

let x: IX = {
    a: 'abc'
};
您可以根据需要提供值:

x.a = 'xyz'
x.b = 123
x.c = new AnotherType()

可以使用类实现接口,然后可以在构造函数中初始化成员:

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
    }
}
deepCopy = <T extends {}>(input: any): T => {
  return JSON.parse(JSON.stringify(input));
};
然后你可以简单地:

var ix: IX = null;
...

ix = new IXClass(...);
// or
ix = ixFactory(...);

您不能在接口中设置默认值,但可以通过使用可选属性来完成您想要做的事情(比较第3段):

只需将界面更改为:

interface IX {
    a: string,
    b?: any,
    c?: AnotherType
}
然后,您可以执行以下操作:

let x: IX = {
    a: 'abc'
}

如果未设置这些属性,请使用init函数为
x.b
x.c
分配默认值。

而@Timar的答案对于
null
默认值(所要求的)非常有效,这里是另一个允许其他默认值的简单解决方案:定义一个选项接口以及包含默认值的相应常量;在构造函数中,使用设置
选项
成员变量

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();
let x : IX = deepCopy(XDef);
输出:

set
null
1

我是在寻找一种比我所得到的更好的方法时偶然发现这一点的。在阅读了答案并尝试了它们之后,我认为我所做的是值得的,因为其他答案对我来说并不简洁。每次设置新接口时,只需编写少量代码对我来说很重要。我决定

使用自定义通用deepCopy函数:

function ixFactory(a: string, b: any, c: AnotherType): IX {
    return {
        a: a,
        b: b,
        c: c
    }
}
deepCopy = <T extends {}>(input: any): T => {
  return JSON.parse(JSON.stringify(input));
};
。。。并在单独的常量中定义默认值

const XDef : IX = {
    a: '',
    b: null,
    c: null,
};
然后像这样初始化:

const x = new ClassX({ a: "set" });
x.printOptions();
let x : IX = deepCopy(XDef);
这就是所需要的

。。但是..

如果要自定义初始化任何根元素,可以修改deepCopy函数以接受自定义默认值。该功能变为:

deepCopyAssign = <T extends {}>(input: any, rootOverwrites?: any): T => {
  return JSON.parse(JSON.stringify({ ...input, ...rootOverwrites }));
};
任何其他首选的深度复制方式都可以。如果只需要一个浅拷贝,那么Object.assign就足够了,不再需要实用工具
deepCopy
deepcopysign
功能

let x : IX = object.assign({}, XDef, { a:'customInitValue' });
已知问题

  • 它不会以这种形式进行深度分配,但也不太难实现 修改
    deepcopysign
    以在分配之前迭代并检查类型
  • 解析/字符串化过程将丢失函数和引用。 我的任务不需要这些,OP也不需要
  • 执行时,IDE或检查的类型不会提示自定义init值

您可以使用文档中解释的
部分
映射类型:

在您的示例中,您将有:

interface IX {
    a: string;
    b: any;
    c: AnotherType;
}

let x: Partial<IX> = {
    a: 'abc'
}
接口IX{
a:弦;
b:有;
c:另一种类型;
}
设x:Partial={
a:‘abc’
}
我的解决方案:

我已经在Object.assign上创建了包装器来修复键入问题

导出函数赋值(…参数:T[]|部分[]):T{ 返回Object.assign.apply(对象,[{},…args]); } 用法:

环境基地

导出接口环境值{
导出接口环境值{
isBrowser:布尔型;
apiURL:字符串;
}
导出常量EnvironmentBaseValues:部分={
isBrowser:typeof window!==“未定义”,
};
导出默认的EnvironmentBaseValue;
环境发展小组

从“/env.base”导入{EnvironmentValues,environmentbasevalues};
从“../utilities”导入{assign};
export const environmentdevvalues:EnvironmentValues=assign(
{
APIRL:“/api”,
},
环境基本值
);
导出默认环境devvalue;

您可以使用两个单独的配置。一个作为具有可选属性(将具有默认值)的输入,另一个仅具有必需的属性。这可以通过
&
必需的
实现:

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({});
接口DefaultedFuncConfig{
b?:布尔型;
}
接口MandatoryFuncConfig{
a:布尔型;
}
导出类型FuncConfig=MandatoryFuncConfig&DefaultedFuncConfig;
导出常量func=(配置:FuncConfig):必需=>({
b:是的,
…配置
});
//将编译
func({a:true});
func({a:true,b:true});
//会出错吗
func({b:true});
func({});

这取决于大小写和用法。通常,在TypeScript中,接口没有默认值

如果不使用默认值
您可以将
x
声明为:

let x: IX | undefined; // declaration: x = undefined
然后,在init函数中可以设置实际值:

x = {
    a: 'xyz'
    b: 123
    c: new AnotherType()
};
这样,
x
可以是未定义的或已定义的-
未定义的
表示对象未初始化,如果不需要,则不设置默认值。这在逻辑上优于定义“垃圾”

如果要部分分配对象:
您可以使用以下可选属性定义类型:

interface IX {
    a: string,
    b?: any,
    c?: AnotherType
}
在这种情况下,您只需设置
a
。其他类型用
标记,这意味着它们是可选的,默认值为
未定义

甚至

let x: Partial<IX> = { ... }
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                    
}