我在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 IX {
  a: String = '';
  b?: any;
  c: Cee = new Cee();
}

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

其他回答

您可以使用两个单独的配置。一个作为具有可选属性的输入(将具有默认值),另一个仅具有必需的属性。这可以通过&和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({});

在有很多参数的情况下,最好让用户只插入几个参数,而且没有特定的顺序。

例如,不好的做法:

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                    
    })

跟进: 在前面的函数定义中,我们为参数对象的字段定义了默认值,但没有为对象本身定义默认值。 因此,我们将从下面的调用中得到一个提取错误(例如不能读取未定义的属性'b'):

foo()

有两种可能的解决方案:

1.

const defaultObject = {a=undefined, b=1, c=99, d=88, e=undefined}
function foo({a=defaultObject.a, b=defaultObject.b, c=defaultObject.c, d=defaultObject.d, e=defaultObject.e} = defaultObject)
function foo(object = {}) {
    object = { b=1, c=99, d=88, ...object }
    //Continue the function code..
}

跟进: 如果你需要类型和默认值(并且你不想声明一个接口),你可以这样写:

function foo(params: {a?: string, b?: number, c?: number, d?: number, e?: string}) {
    params = { b:1, c:99, d:88, ...params }
    //Continue the function code..
}

我使用下面的模式:

创建工具类型默认值<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 }

我能告诉接口默认属性我不提供为空吗?是什么让我这么做的

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

替代

但是没有指定的值在JavaScript运行时默认为undefined。所以你可以把它们标记为可选:

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

现在当你创建它时,你只需要提供一个:

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

你可以根据需要提供这些值:

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

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