Я хотел бы написать функцию в Typescript следующим образом
x, y => void
Где x
— массив типа string[]
, а y
— массив типа string[][]
, но я хотел бы ограничить y
, чтобы внутренние массивы имели ту же длину, что и массив x
.
Это возможно?
ОБНОВЛЕНИЕ 2022 Это можно сделать гораздо проще и менее многословно:
function array<X extends unknown[], Y extends X[]>(x: [...X], y: [...Y]) {
return [1, 2, 3] as any
}
const result = array([1, 2, 3], [[1, 1, 1]]) // ok
const result2 = array([1, 2, 3], [[1, 1, 1], [1, 1]]) // expected error
const result3 = array([1, 2, 3], [[1, 1, 1], [1]]) // expected error
const result4 = array([1, 2], [[1, 1], [1, 1]]) // ok
const result5 = array([1, 2], [[1, 1], [1]]) // expected error
Да, это возможно.
СЧИТАЙТЕ ЭТО РЕШЕНИЕ КАК УСТАРЕВШИМ
Вот мое решение:
/**
* Second approach, is more advanced and it is exactly what you are looking for
*/
type ArrayElement = number
type Array1D = ReadonlyArray<ArrayElement>
type Array2D = ReadonlyArray<Array1D>
type MutableLength = unknown[]['length'] // number
/**
* Get length of the array
* Allow only immutable arrays
*/
export type Length<T extends ReadonlyArray<any>> = T extends { length: infer L } ? MutableLength extends L ? MutableLength : L : MutableLength;
/**
* Compare length of the arrays
*/
type CompareLength<X extends ReadonlyArray<any>, Y extends ReadonlyArray<any>> =
MutableLength extends Length<X> ? false : MutableLength extends Length<Y> ? false : Length<X> extends Length<Y> ? true : false;
/**
* Check if all arrays (union type) have same length as X
*/
type Filter<X extends ReadonlyArray<any>, Y extends ReadonlyArray<any>> =
X['length'] extends Y['length']
? Y['length'] extends X['length'] ? Y : never : never
{
/**
* CompareLength, compares length of X and filtered Y,
* if true - return zero index element - ReadonlyArray<ArrayElement>
* if false - return never
*
* So, if it will return never, then you can't pass second argument,
* but if you did not provide second argument, you will receive another error -
* function expects two arguments
*/
function array<X extends Array1D, Y extends {
0: readonly ArrayElement[]
}[CompareLength<X, Filter<X, Y>> extends true ? 0 : never]>(x: X, y: readonly Y[]): 'put here your returned type'
function array<X extends Array1D, Y extends readonly ArrayElement[], Z extends CompareLength<X, Y>>(x: X, y: readonly Y[]) {
return [1, 2, 3] as any
}
const result = array([1, 2, 3] as const, [[1, 1, 1], [1, 2, 3]] as const) // ok
const result0 = array([1, 2, 3] as const, [[1, 1, 1]] as const) // ok
const arr = [1, 2, 3] as const
const result1 = array([1, 2, 3], [[1, 1, 1], [1]]) // error
const result2 = array([1, 2, 3] as const, [[1, 1, 1], [1, 2]] as const) // error
const result3 = array([1, 2, 3] as const, [[1, 2]] as const) // error
const result4 = array([1, 2, 3] as const, [[1], [1, 2], [1, 2, 3]] as const) // error
const result5 = array([1, 2, 3] as const, [1] as const) // error
const result6 = array([1, 2, 3] as const, [[1, 2, 3], []] as const) // error
const result7 = array(arr, [[1, 1, 1]]) // error, because TS is unable to fidure out length of mutable array.
}
Я использовал number
вместо string
, потому что проще вводить числа, а не строки.
Смело меняйте ArrayElement
на string
Пожалуйста, имейте в виду, что вы должны использовать каждый раз as const
для определения длины массива.