Skip to content

Commit

Permalink
fix(types): improve the isArray return type for unknown input type (
Browse files Browse the repository at this point in the history
  • Loading branch information
aleclarson committed Jul 4, 2024
1 parent 94543a4 commit ef14440
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 11 deletions.
16 changes: 7 additions & 9 deletions src/async/all.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,11 @@ export async function all<T extends Record<string, Promise<any>>>(
promises: T,
): Promise<{ [K in keyof T]: Awaited<T[K]> }>

export async function all<
T extends Record<string, Promise<any>> | Promise<any>[],
>(promises: T) {
export async function all(
promises: Record<string, Promise<any>> | Promise<any>[],
): Promise<any> {
const entries = isArray(promises)
? promises.map(p => [null, p] as [null, Promise<any>])
? promises.map(p => [null, p] as const)
: Object.entries(promises)

const results = await Promise.all(
Expand All @@ -63,16 +63,14 @@ export async function all<
}

if (isArray(promises)) {
return results.map(r => r.result) as T extends Promise<any>[]
? PromiseValues<T>
: unknown
return results.map(r => r.result)
}

return results.reduce(
(acc, item) => {
acc[item.key as keyof T] = item.result
acc[item.key!] = item.result
return acc
},
{} as { [K in keyof T]: Awaited<T[K]> },
{} as Record<string, any>,
)
}
2 changes: 2 additions & 0 deletions src/mod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,3 +107,5 @@ export * from './typed/isPrimitive.ts'
export * from './typed/isPromise.ts'
export * from './typed/isString.ts'
export * from './typed/isSymbol.ts'

export * from './types'
13 changes: 11 additions & 2 deletions src/typed/isArray.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,11 @@
export const isArray: (value: unknown) => value is readonly any[] =
Array.isArray
import type { ExtractNotAny } from 'radashi'

export const isArray = Array.isArray as <Input>(
value: Input,
) => value is readonly any[] extends ExtractNotAny<Input, readonly any[]>
? Extract<Input, readonly any[]>
: any[] extends ExtractNotAny<Input, any[]>
? Extract<Input, any[]>
: unknown[] extends Input
? unknown[]
: never
25 changes: 25 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
declare const any: unique symbol

/**
* The `Any` class does not exist at runtime. It's used in type
* definitions to detect an `any` type.
*
* ```ts
* type IsAny<T> = [T] extends [Any] ? 'is any' : 'is not any'
* ```
*/
export declare class Any {
private any: typeof any
}

/**
* Extracts `T` if `T` is not `any`, otherwise `never`.
*
* ```ts
* type A = ExtractNotAny<any, string>
* // ^? never
* type B = ExtractNotAny<string | number, string>
* // ^? string
* ```
*/
export type ExtractNotAny<T, U> = Extract<[T] extends [Any] ? never : T, U>
52 changes: 52 additions & 0 deletions tests/typed/isArray.test-d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import * as _ from 'radashi'

describe('isArray return type', () => {
test('value is any', () => {
const value = {} as any
if (_.isArray(value)) {
expectTypeOf(value).toEqualTypeOf<unknown[]>()
} else {
expectTypeOf(value).toEqualTypeOf<any>()
}
})
test('value is unknown', () => {
const value = {} as unknown
if (_.isArray(value)) {
expectTypeOf(value).toEqualTypeOf<unknown[]>()
} else {
expectTypeOf(value).toEqualTypeOf<unknown>()
}
})
test('value is string', () => {
const value = {} as string
if (_.isArray(value)) {
expectTypeOf(value).toEqualTypeOf<never>()
} else {
expectTypeOf(value).toEqualTypeOf<string>()
}
})
test('value is string or ReadonlyMap', () => {
const value = {} as string | readonly string[]
if (_.isArray(value)) {
expectTypeOf(value).toEqualTypeOf<readonly string[]>()
} else {
expectTypeOf(value).toEqualTypeOf<string>()
}
})
test('value is string, ReadonlyMap, or Map', () => {
const value = {} as string | readonly string[] | string[]
if (_.isArray(value)) {
expectTypeOf(value).toEqualTypeOf<readonly string[] | string[]>()
} else {
expectTypeOf(value).toEqualTypeOf<string>()
}
})
test('value is string or Map', () => {
const value = {} as string | string[]
if (_.isArray(value)) {
expectTypeOf(value).toEqualTypeOf<string[]>()
} else {
expectTypeOf(value).toEqualTypeOf<string>()
}
})
})

0 comments on commit ef14440

Please sign in to comment.