type-plus
Version:
Provides additional types for TypeScript.
72 lines (67 loc) • 1.92 kB
text/typescript
import type { NotExtendable } from './Extends.js'
import type { IsEmptyObject } from './IsEmptyObject.js'
/**
* Can `A` assign to `B`
*
* Note that when union is involved, the assignability is measured distributively.
* Meaning the result can be `Then | Else` (i.e. `boolean` by default),
* instead of distinctive `Then` (`true`) or `Else` (`false`).
*
* This is the correct behavior.
*
* @example
* ```ts
* CanAssign<number | string, number> // boolean
* ```
*
* We are checking can `A` assign to `B`.
* Since `A` is `number | string`,
* `A` can assign to `B` when `A` is number` (true), and
* `A` cannot assign to `B` when `A` is string` (false).
* So the result is `true | false = boolean`.
*
* If you want to make sure all branches are assignable,
* use `StrictCanAssign<A, B>`.
*/
export type CanAssign<A, B, Then = true, Else = false> = IsEmptyObject<A> extends true
? Record<string, unknown> extends B
? Then
: Else
: boolean extends A
? boolean extends B
? Then
: Else
: A extends B
? Then
: Else
/**
* Can `A` strictly assign to `B`.
*
* All branches in an union `A` are assignable to `B`.
*
* @example
* ```ts
* StrictCanAssign<number | string, number> // false
* StrictCanAssign<number | string, number | string> // true
* ```
*/
export type StrictCanAssign<A, B, Then = true, Else = false> = IsEmptyObject<A> extends true
? Record<string, unknown> extends B
? Then
: Else
: boolean extends A
? boolean extends B
? Then
: Else
: [A] extends [B]
? Then
: Else
/**
* @deprecated use `CanAssign` instead
*/
export type IsAssign<A, B, Then = true, Else = false> = CanAssign<A, B, Then, Else>
export function canAssign<T>(canAssign: false): <S>(subject: NotExtendable<S, T>) => true
export function canAssign<T>(): <S extends T>(subject: S) => CanAssign<S, T>
export function canAssign<T>(): <S extends T>(subject: S) => any {
return () => true
}