#typescript
#typescript
Вопрос:
Мне нужно создать объект следующего типа:
type ArrayWithA = [number, number, number] amp; { a: string };
Я делаю это следующим образом:
const obj : any = [1, 2, 3];
obj.a = "foo";
const arrayWithA : ArrayWithA = obj as ArrayWithA;
Вопрос: Каков лучший способ добиться этого (т.е. Без использования any
)?
Бонусный вопрос: Каков хороший способ инициализации объекта типа: type FuncWithA = ((string)=>void) amp; { a: string }
?
Ответ №1:
Я бы рекомендовал использовать Object.assign()
, который стандартная библиотека TypeScript представляет как возвращающий тип пересечения нужной вам формы. Есть небольшая проблема в том, что нелегко заставить компилятор просто сделать вывод, что литерал массива будет кортежем точного типа [number, number, number]
. Если вы согласны с readonly [number, number, number]
этим, вы можете использовать const
утверждение:
type ArrayWithA = readonly [number, number, number] amp; { a: string };
const arrayWithA: ArrayWithA = Object.assign([1, 2, 3] as const, { a: "foo" });
В противном случае вы можете использовать различные приемы:
type ArrayWithA = [number, number, number] amp; { 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) amp; { 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
Ответ №2:
Используйте Object.assign
!
Возвращаемый тип Object.assign
— это просто пересечение типов его аргументов, поэтому вы можете создавать эти гибридные объекты, перечисляя их части и комбинируя сразу, а не добавляя дополнительные свойства постфактум (что часто требует приведения типов, как вы заметили).
Итак, для некоторых ваших примеров вы можете сделать это следующим образом:
type ArrayWithA = [number, number, number] amp; { 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] amp; { a: string } and is assignable to ArrayWithA.
// Same for functions!
type FuncWithA = ((arg: string) => void) amp; { a: string };
const f1 = Object.assign((s: string) => {}, {a: "foo"}) // f1 has type: ((s: string) => void) amp; { a: string } and is assignable to FuncWithA.
Ответ №3:
Я бы выбрал что-то вроде:
type ArrayWithA = [number, number, number] amp; { 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);