Это трудная задача при реализации обобщенных функций c, где параметр типа generi c имеет тип объединения. См. microsoft / TypeScript # 13995 для получения дополнительной информации. Основная проблема c заключается в том, что, хотя может иметь возможность для компилятора сузить тип arg.source
, скажем, Int32Array
, компилятор не может сузить генерит c введите параметр от T extends TypedArray
до чего-то вроде T extends Int32Array
. В general сужать T
небезопасно, когда вы сужаете значение типа T
, но во многих случаях, подобных вашему, было бы очень удобно сделать это. Пока что этого не происходит.
Чтобы взять существующую реализацию и заставить ее скомпилировать, вы можете использовать утверждения типа так, как вы это сделали.
Если бы это было все, что я собирался сказать, я бы оставил другой ответ в силе, но я хотел бы показать, что если вы хотите превратить свою реализацию в крендельки для руководства компилятором через вашу логику c вы можете получить что-то, что по крайней мере неопределенно безопасно для типов без утверждения:
const typedArrayMaker = (size: number) => ({
get Int32Array() { return new Int32Array(size); },
get Float32Array() { return new Float32Array(size); },
get Float64Array() { return new Float64Array(size); }
});
export function createTypedArray<K extends keyof ReturnType<typeof typedArrayMaker>>(
arg: { source: { [Symbol.toStringTag]: K }, arraySize: number }) {
return typedArrayMaker(arg.arraySize)[arg.source[Symbol.toStringTag]];
}
Здесь мы создаем функцию typedArrayMaker
, которая при заданном размере возвращает объект с getter методов, по одному для каждого типа массива, который вы хотите создать. Компилятор видит тип typedArrayMaker
как
const typedArrayMaker: (size: number) => {
readonly Int32Array: Int32Array;
readonly Float32Array: Float32Array;
readonly Float64Array: Float64Array;
}
Затем функция createTypedArray()
принимает тот же аргумент, что и раньше, но параметр generi c равен K
, значение [Symbol.toStringTag]
свойство arg.source
. Известно, что все типизированные массивы имеют такое значение строковый литерал для этого свойства. И это то, что мы используем для индексации в typedArrayMaker
.
Все это превращает возвращаемый тип в обобщенную c индексацию операцию, которую он может на самом деле рассуждать. В итоге вы получите тип возврата, который зависит от K
, без ошибок. Давайте проверим это:
function test(i32: Int32Array, f32: Float32Array, f64: Float64Array, i8: Int8Array) {
const i32New = createTypedArray({ source: i32, arraySize: 128 }); // Int32Array
const f32New = createTypedArray({ source: f32, arraySize: 128 }); // Float32Array
const f64New = createTypedArray({ source: f64, arraySize: 128 }); // Float64Array
const i8New = createTypedArray({ source: i8, arraySize: 128 }); // error!
// ----------------------------> ~~~~~~
// Type '"Int8Array"' is not assignable to type
// '"Int32Array" | "Float32Array" | "Float64Array"'
}
Вы можете видеть, что компилятор распознает, что i32New
, f32New
и f64New
относятся к ожидаемым типам, тогда как i8
вызывает ошибку при передаче в createTypedArray()
, поскольку этот типизированный тип массива еще не включен в нашу функцию.
Просто подумал, что было бы интересно показать один способ сделать это так, как понимает компилятор. На практике я бы на 100% определенно рекомендовал использовать утверждение типа, потому что свойства функций более высокого порядка с получателями и символами безумны.
Хорошо, надеюсь, это поможет; удачи!
Детская площадка ссылка на код