我在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”在类型“{}”中缺失。


当前回答

这要看情况和用法而定。通常,在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作为默认值,这只是取决于你的用例。

其他回答

虽然@Timar的答案是完美的空默认值(什么是被要求的),这里有另一个简单的解决方案,允许其他默认值:定义一个选项接口,以及一个根据常量包含默认值;在构造函数中,使用展开操作符设置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

你不能在接口中设置默认值,但是你可以通过使用可选属性来完成你想做的事情:

简单地将界面更改为:

interface IX {
    a: string,
    b?: any,
    c?: AnotherType
}

你可以这样做:

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

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

这要看情况和用法而定。通常,在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作为默认值,这只是取决于你的用例。

我们正在努力解决这个问题。使用类而不是接口。

class IX {
  a: String = '';
  b?: any;
  c: Cee = new Cee();
}

class Cee {
  c: String = 'c';
  e: String = 'e';
}

这是我在寻找比我已经得到的更好的方法时偶然发现的。在阅读了答案并尝试了它们之后,我认为值得把我正在做的事情发布出来,因为其他答案对我来说感觉不那么简洁。对我来说,每次设置新界面时只需要编写少量代码是很重要的。我选定了……

使用自定义通用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提示或类型检查。