开发者问题收集

如何在 TypeScript 中使用额外属性初始化数组对象?

2021-01-12
142

我需要创建以下类型的对象:

type ArrayWithA = [number, number, number] & { a: string };

我按如下方式执行此操作:

const obj : any = [1, 2, 3];
obj.a = "foo";
const arrayWithA : ArrayWithA = obj as ArrayWithA;

问题: 有什么更好的方法可以实现此目的(即不使用 any )?

附加问题: 有什么好方法可以初始化以下类型的对象: type FuncWithA = ((string)=>void) & { a: string >

3个回答

我建议使用 Object.assign() TypeScript 标准库 将其表示为返回所需形式的交集类型。有一点麻烦的是,很难让编译器推断数组文字将是精确类型为 [number, number, number] 的元组。如果您对 readonly [number, number, number] 感到满意,则可以使用 const 断言 :

type ArrayWithA = readonly [number, number, number] & { a: string };
const arrayWithA: ArrayWithA = Object.assign([1, 2, 3] as const, { a: "foo" });

否则,您可以使用各种技巧:

type ArrayWithA = [number, number, number] & { a: string };

const arr: [number, number, number] = [1, 2, 3]; // annotate extra variable
let arrayWithA: ArrayWithA = Object.assign(arr, { a: "foo" });

// type assertion
arrayWithA = Object.assign([1, 2, 3] as [number, number, number], { a: "foo" });

// helper function
const asTuple = <T extends any[]>(arr: [...T]) => arr;
arrayWithA = Object.assign(asTuple([1, 2, 3]), { a: "foo" });

对于函数,您可以使用 Object.assign() 执行相同的操作:

type FuncWithA = ((x: string) => void) & { a: string }
let funcWithA: FuncWithA = Object.assign(
  (x: string) => console.log(x.toUpperCase()), 
  { a: "foo" }
);

但您也可以只使用函数语句,稍后再添加属性,因为 TypeScript 3.1 引入了 扩展函数

function func(x: string) {
    console.log(x);
}
func.a = "foo"; // no error
funcWithA = func; // that works

Playground 代码链接

jcalz
2021-01-12

使用 Object.assign

Object.assign 的返回类型只是其参数类型的交集,因此您可以通过列出它们的各部分并立即组合来组成这些混合对象,而不是事后添加其他属性(这通常需要类型转换,正如您所注意到的)。

因此,对于您的某些示例,您可以这样做:

type ArrayWithA = [number, number, number] & { a: string };

// Try like so:
// The cast is needed as otherwise it will be inferred as number[].
const obj2 = Object.assign([1, 2, 3] as [number, number, number], {a: "foo"}); // obj2 has type: [number, number, number] & { a: string } and is assignable to ArrayWithA.

// Same for functions!
type FuncWithA = ((arg: string) => void) & { a: string };

const f1 = Object.assign((s: string) => {}, {a: "foo"}) // f1 has type: ((s: string) => void) & { a: string } and is assignable to FuncWithA.

游乐场链接。

CRice
2021-01-12

我会选择类似这样的方法:

type ArrayWithA = [number, number, number] & { a: string };
namespace ArrayWithA {
  export function of(a: string, ...rest: [number, number, number]): ArrayWithA {
    const o = rest as ArrayWithA;
    o.a = a;
    return o;
  }
}

const arrayWithA = ArrayWithA.of('a', 1, 2, 3);
Tomasz Gawel
2021-01-12