A suggestion I would make is to utilize the Object.assign()
method, which is represented in TypeScript's standard library as returning an intersection type similar to what you require. However, there is a bit of complexity in getting the compiler to infer that an array literal will be exactly like the type [number, number, number]
. If you are fine with using
readonly [number, number, number]
, then you can employ a
const
assertion:
type ArrayWithA = readonly [number, number, number] & { a: string };
const arrayWithA: ArrayWithA = Object.assign([1, 2, 3] as const, { a: "foo" });
If not, there are several techniques you can try:
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" });
When it comes to functions, you can apply the same principle with Object.assign()
:
type FuncWithA = ((x: string) => void) & { a: string }
let funcWithA: FuncWithA = Object.assign(
(x: string) => console.log(x.toUpperCase()),
{ a: "foo" }
);
Alternatively, you could just use a function statement and add the property later, especially since TypeScript 3.1 introduced expando functions:
function func(x: string) {
console.log(x);
}
func.a = "foo"; // no error
funcWithA = func; // works perfectly fine
Link to code on Playground