|
| 1 | +import {expectType} from 'tsd'; |
| 2 | +import type {SetNonNullableDeep} from '../source/set-non-nullable-deep'; |
| 3 | + |
| 4 | +expectType<{a: number}>({} as SetNonNullableDeep<{a: number | null}, 'a'>); |
| 5 | +expectType<{a: number}>({} as SetNonNullableDeep<{a: number | undefined}, 'a'>); |
| 6 | +expectType<{a: number}>({} as SetNonNullableDeep<{a: number | null | undefined}, 'a'>); |
| 7 | +expectType<{a?: number}>({} as SetNonNullableDeep<{a?: number | null}, 'a'>); |
| 8 | +expectType<{a?: number}>({} as SetNonNullableDeep<{a?: number | undefined}, 'a'>); |
| 9 | +expectType<{a?: number}>({} as SetNonNullableDeep<{a?: number | null | undefined}, 'a'>); |
| 10 | + |
| 11 | +expectType<{a: number; b: {c: string}}>({} as SetNonNullableDeep<{a: number; b: {c: string | null}}, 'b.c'>); |
| 12 | +expectType<{a: number; b: {c: string}}>({} as SetNonNullableDeep<{a: number; b: {c: string | undefined}}, 'b.c'>); |
| 13 | +expectType<{a: number; b: {c: string}}>({} as SetNonNullableDeep<{a: number; b: {c: string | null | undefined}}, 'b.c'>); |
| 14 | +expectType<{a: number; b: {c?: string}}>({} as SetNonNullableDeep<{a: number; b: {c?: string | null}}, 'b.c'>); |
| 15 | +expectType<{a: number; b: {c?: string}}>({} as SetNonNullableDeep<{a: number; b: {c?: string | undefined}}, 'b.c'>); |
| 16 | +expectType<{a: number; b: {c?: string}}>({} as SetNonNullableDeep<{a: number; b: {c?: string | null | undefined}}, 'b.c'>); |
| 17 | + |
| 18 | +// Becomes `never` when the value is only `null` or `undefined` |
| 19 | +expectType<{a: never}>({} as SetNonNullableDeep<{a: null | undefined}, 'a'>); |
| 20 | +expectType<{a?: never}>({} as SetNonNullableDeep<{a?: null | undefined}, 'a'>); |
| 21 | + |
| 22 | +// Ignores keys that are already non-nullable |
| 23 | +expectType<{a: number}>({} as SetNonNullableDeep<{a: number}, 'a'>); |
| 24 | +expectType<{readonly a?: number}>({} as SetNonNullableDeep<{readonly a?: number}, 'a'>); |
| 25 | +expectType<{a?: number; b: {c: string}}>({} as SetNonNullableDeep<{a?: number; b: {c: string}}, 'b.c'>); |
| 26 | +expectType<{a: number; readonly b?: {c?: string}}>({} as SetNonNullableDeep<{a: number; readonly b?: {c?: string}}, 'b.c'>); |
| 27 | + |
| 28 | +// Removes nullables only from the specified keys, nullables in other paths are preserved |
| 29 | +expectType<{a: number; b: {c: string | null | undefined} | null | undefined}>( |
| 30 | + {} as SetNonNullableDeep<{a: number | null | undefined; b: {c: string | null | undefined} | null | undefined}, 'a'>, |
| 31 | +); |
| 32 | +expectType<{a: number | null | undefined; b: {c: string | null | undefined}}>( |
| 33 | + {} as SetNonNullableDeep<{a: number | null | undefined; b: {c: string | null | undefined} | null | undefined}, 'b'>, |
| 34 | +); |
| 35 | +expectType<{a: number | null | undefined; b: {c: string} | null | undefined}>( |
| 36 | + {} as SetNonNullableDeep<{a: number | null | undefined; b: {c: string | null | undefined} | null | undefined}, 'b.c'>, |
| 37 | +); |
| 38 | +expectType<{a: {b: string}}>({} as SetNonNullableDeep<{a: {b: string | null} | null | undefined}, 'a' | 'a.b'>); |
| 39 | +expectType<{a: {b: {c: string}} | null | undefined}>({} as SetNonNullableDeep<{a: {b: {c: string | undefined} | null} | null | undefined}, 'a.b' | 'a.b.c'>); |
| 40 | +expectType<{a: {b: {c: string} | null}}>({} as SetNonNullableDeep<{a: {b: {c: string | undefined} | null} | null | undefined}, 'a' | 'a.b.c'>); |
| 41 | +expectType<{a: {b?: {c: {d?: {e: number | string}; f: {g: boolean}} | undefined} | null; h: number | null}}>( |
| 42 | + {} as SetNonNullableDeep< |
| 43 | + {a: {b?: {c: {d?: {e: number | string} | null; f: {g: boolean | null}} | undefined} | null; h: number | null} | undefined}, |
| 44 | + 'a' | 'a.b.c.d' | 'a.b.c.f.g' |
| 45 | + >, |
| 46 | +); |
| 47 | + |
| 48 | +// Unions |
| 49 | +expectType<{a: string} | {a?: number} | {a: boolean}>({} as SetNonNullableDeep<{a: string | null} | {a?: number | undefined} | {a: boolean | null | undefined}, 'a'>); |
| 50 | +expectType<{a: string; b: number}>({} as SetNonNullableDeep<{a: string | null; b: number | undefined}, 'a' | 'b'>); |
| 51 | +expectType<{a: string; b: {c: {d: {e: number}} | null} | undefined}>( |
| 52 | + {} as SetNonNullableDeep<{a: string | null; b: {c: {d: {e: number | undefined}} | null} | undefined}, 'a' | 'b.c.d.e'>, |
| 53 | +); |
| 54 | +expectType<{a: {b: string | null}} | {a?: number; b: {c?: string}}>( |
| 55 | + {} as SetNonNullableDeep<{a: {b: string | null} | undefined} | {a?: number | undefined; b: {c?: string}}, 'a' | 'b.c'>, |
| 56 | +); |
| 57 | +expectType<{a: string; b: {c?: {d: string} | undefined; f?: number}} | {a: {b: number}; c: never; d?: undefined}>( |
| 58 | + {} as SetNonNullableDeep< |
| 59 | + {a: string; b: {c?: {d: string | null} | undefined; f?: number} | null} | {a: {b: number | null}; c: null; d?: undefined}, |
| 60 | + 'b' | 'b.c.d' | 'a.b' | 'c' |
| 61 | + >, |
| 62 | +); |
| 63 | +expectType<{a: 1; b: {c: 2}; d?: {e?: {f?: 2}; g?: 3}}>( |
| 64 | + {} as SetNonNullableDeep<{a: 1 | null; b: {c: 2 | null}; d?: {e?: {f?: 2 | null}; g?: 3}}, 'a' | 'b' | 'b.c' | 'd.e.f' | 'd.g'>, |
| 65 | +); |
| 66 | +expectType<{a: {b: string} | {c: string} | {b: {c: string | null}}}>( |
| 67 | + {} as SetNonNullableDeep<{a: {b: string | null} | {c: string} | {b: {c: string | null} | undefined}}, 'a.b'>, |
| 68 | +); |
| 69 | + |
| 70 | +// Preserves non-nullable values when they are in a union with objects |
| 71 | +expectType<{a?: {b: string} | number}>({} as SetNonNullableDeep<{a?: {b: string} | number | null | undefined}, 'a'>); |
| 72 | +expectType<{a: {b: Array<{c?: number | null}> | number}}>({} as SetNonNullableDeep<{a: {b: Array<{c?: number | null}> | number | null}}, 'a.b'>); |
| 73 | +expectType<{a: {b: string | number} | number}>({} as SetNonNullableDeep<{a: {b: string | number | null} | number | null | undefined}, 'a' | 'a.b'>); |
| 74 | +expectType<{a: number; b: {c: number | null} | {d: string}}>({} as SetNonNullableDeep<{a: number; b: {c: number | null} | {d: string | undefined} | null}, 'b' | 'b.d'>); |
| 75 | + |
| 76 | +// Preserves `readonly` modifier |
| 77 | +expectType<{a: string; b: {readonly c: number}}>({} as SetNonNullableDeep<{a: string; b: {readonly c: number | null}}, 'b.c'>); |
| 78 | +expectType<{readonly a: string; readonly b: {c: number | null}}>({} as SetNonNullableDeep<{readonly a: string | null; readonly b: {c: number | null} | null}, 'a' | 'b'>); |
| 79 | +expectType<{readonly a: string; readonly b: {readonly c: number}}>({} as SetNonNullableDeep<{readonly a: string | null; readonly b: {readonly c: number | null} | null}, 'a' | 'b' | 'b.c'>); |
| 80 | + |
| 81 | +// Number keys |
| 82 | +expectType<{0: 1; 1: {2?: string}}>({} as SetNonNullableDeep<{0: 1; 1: {2?: string | null}}, '1.2'>); |
| 83 | +expectType<{0: 1; 1?: {2: string | null}}>({} as SetNonNullableDeep<{0: 1 | null; 1?: {2: string | null} | undefined}, 0 | 1>); |
| 84 | + |
| 85 | +// Number keys containing dots |
| 86 | +// NOTE: Passing "1.2" instead of 1.2 will treat it as a path instead of a key |
| 87 | +expectType<{1.2?: string; 1?: {2?: string | null} | null}>({} as SetNonNullableDeep<{1.2?: string | null; 1?: {2?: string | null} | null}, 1.2>); |
| 88 | +expectType<{1.2?: string | null; 1?: {2?: string} | null}>({} as SetNonNullableDeep<{1.2?: string | null; 1?: {2?: string | null} | null}, '1.2'>); |
| 89 | +expectType<{1.2?: string; 1?: {2?: string} | undefined}>({} as SetNonNullableDeep<{1.2?: string | undefined; 1?: {2?: string | undefined} | undefined}, 1.2 | '1.2'>); |
| 90 | + |
| 91 | +// Index signatures |
| 92 | +expectType<{[x: string]: any; a: number; b: {c: number}}>({} as SetNonNullableDeep<{[x: string]: any; a: number | null; b: {c: number | null}}, 'a' | 'b.c'>); |
| 93 | + |
| 94 | +// Works with `KeyPaths` containing template literals |
| 95 | +expectType<{a: number | null; b: {c: number} | {d: number} | null | undefined}>({} as SetNonNullableDeep<{a: number | null; b: {c: number | null} | {d: number | undefined} | null | undefined}, `b.${'c' | 'd'}`>); |
| 96 | +expectType<{a: number; b: null | {readonly c: {1: number[]} | undefined} | {d: {1: number[]} | null}}>({} as SetNonNullableDeep< |
| 97 | +{a: number | undefined; b: null | {readonly c: {1: number[] | undefined} | undefined} | {d: {1: number[] | undefined} | null}}, 'a' | `b.${'c' | 'd'}.1` |
| 98 | +>); |
| 99 | + |
| 100 | +// Non recursive types |
| 101 | +expectType<{a: {b: never} | Set<number | null>}>({} as SetNonNullableDeep<{a: {b: null} | Set<number | null>}, 'a.b'>); |
| 102 | +expectType<{a: {b: {c: string} | Map<string, string>}}>({} as SetNonNullableDeep<{a: {b: {c: string} | Map<string, string> | null}}, 'a.b'>); |
| 103 | + |
| 104 | +// === Arrays === |
| 105 | +expectType<[string, number | null, boolean]>({} as SetNonNullableDeep<[string | null, number | null, boolean | undefined], '0' | '2'>); |
| 106 | +expectType<{a?: [string | null, number?, boolean?] | null}>({} as SetNonNullableDeep<{a?: [string | null, (number | undefined)?, (boolean | null)?] | null}, 'a.1' | 'a.2'>); |
| 107 | +expectType<{a: readonly [string, number, (boolean | null)?]}>({} as SetNonNullableDeep<{a: readonly [string, number | undefined, (boolean | null)?]}, 'a.1'>); |
| 108 | +expectType<{readonly a: [string, number | null | undefined, boolean, ...Array<number | null>]}>( |
| 109 | + {} as SetNonNullableDeep<{readonly a: [string | null, number | null | undefined, boolean | undefined, ...Array<number | null>]}, 'a.0' | 'a.2'>, |
| 110 | +); |
| 111 | +expectType<{readonly a?: [string, number, boolean, ...Array<string | null>]}>( |
| 112 | + {} as SetNonNullableDeep<{readonly a?: [string, number | null, boolean | undefined, ...Array<string | null>] | undefined}, 'a' | 'a.1' | 'a.2'>, |
| 113 | +); |
| 114 | + |
| 115 | +// Readonly arrays |
| 116 | +expectType<{a?: {b?: readonly [(string | number)?]} | null}>({} as SetNonNullableDeep<{a?: {b?: readonly [(string | number | null)?]} | null}, 'a.b.0'>); |
| 117 | +expectType<{a: readonly [string, number, boolean, ...Array<string | undefined>] | undefined}>( |
| 118 | + {} as SetNonNullableDeep<{a: readonly [string | null, number | undefined, boolean | null, ...Array<string | undefined>] | undefined}, 'a.0' | 'a.1' | 'a.2'>, |
| 119 | +); |
| 120 | + |
| 121 | +// Ignores `Keys` that are already non-nullable |
| 122 | +expectType<{a: [string, (number | null)?, boolean?]}>({} as SetNonNullableDeep<{a: [string, (number | null)?, boolean?]}, 'a.0'>); |
| 123 | +expectType<{a: [string, number?, boolean?]}>({} as SetNonNullableDeep<{a: [string, (number | null)?, boolean?]}, 'a.0' | 'a.1'>); |
| 124 | + |
| 125 | +// Ignores `Keys` that are not known |
| 126 | +// This case is only possible when the array contains a rest element, |
| 127 | +// because otherwise the constaint on `KeyPaths` would disallow out of bound keys. |
| 128 | +expectType<{a?: readonly [string | null, (number | undefined)?, (boolean | null)?, ...Array<number | null | undefined>] | undefined}>( |
| 129 | + {} as SetNonNullableDeep<{a?: readonly [string | null, (number | undefined)?, (boolean | null)?, ...Array<number | null | undefined>] | undefined}, 'a.10'>, |
| 130 | +); |
| 131 | + |
| 132 | +// Unions of arrays |
| 133 | +expectType<{a: [string] | [string, number?, (boolean | null)?, ...Array<number | null>] | readonly [string, number, (boolean | undefined)?]}>( |
| 134 | + {} as SetNonNullableDeep<{a: [string | undefined] | [string | null, number?, (boolean | null)?, ...Array<number | null>] | readonly [string | null | undefined, number | null, (boolean | undefined)?]}, 'a.0' | 'a.1'>, |
| 135 | +); |
| 136 | + |
| 137 | +// Labelled tuples |
| 138 | +expectType<{a?: [b: string, c: number] | undefined}>({} as SetNonNullableDeep<{a?: [b: string | null, c: number | undefined] | undefined}, 'a.0' | 'a.1'>); |
| 139 | + |
| 140 | +// Non-tuple arrays |
| 141 | +expectType<{a: string[]}>({} as SetNonNullableDeep<{a: Array<string | null>}, `a.${number}`>); |
| 142 | +expectType<{readonly a: ReadonlyArray<string | number>}>({} as SetNonNullableDeep<{readonly a: ReadonlyArray<string | number | null> | undefined}, 'a' | `a.${number}`>); |
| 143 | + |
| 144 | +// Nested arrays |
| 145 | +expectType<{a?: [([string?, (number | null)?] | null)?]}>({} as SetNonNullableDeep<{a?: [([(string | undefined)?, (number | null)?] | null)?] | undefined}, 'a' | 'a.0.0'>); |
| 146 | +expectType<{a?: [[(string | undefined)?, number?]?] | undefined}>({} as SetNonNullableDeep<{a?: [([(string | undefined)?, (number | null)?] | null)?] | undefined}, 'a.0.1' | 'a.0'>); |
| 147 | +expectType<{a?: [[string | null, number]?] | null}>({} as SetNonNullableDeep<{a?: [([string | null, number | undefined] | null)?] | null}, 'a.0' | 'a.0.1'>); |
| 148 | +expectType<{a?: Array<[string | null, number?]> | null}>({} as SetNonNullableDeep<{a?: Array<[string | null, (number | undefined)?]> | null}, `a.${number}.1`>); |
| 149 | + |
| 150 | +// Removes `null` & `undefined` from keys inside arrays |
| 151 | +expectType<{a?: Array<{b: number}> | undefined}>({} as SetNonNullableDeep<{a?: Array<{b: number | null}> | undefined}, `a.${number}.b`>); |
| 152 | +expectType<{readonly a?: [{readonly b: number}]}>({} as SetNonNullableDeep<{readonly a?: [{readonly b: number | undefined}] | null}, 'a' | 'a.0' | 'a.0.b'>); |
| 153 | +expectType<{readonly a: [{readonly b: number}, {c?: string | null}?]}>( |
| 154 | + {} as SetNonNullableDeep<{readonly a: [{readonly b: number | null | undefined}, ({c?: string | null} | undefined)?]}, 'a.0.b' | 'a.1' >, |
| 155 | +); |
| 156 | +expectType<{a?: Array<{b: number; c?: string | null}> | null}>({} as SetNonNullableDeep<{a?: Array<{b: number | undefined; c?: string | null}> | null}, `a.${number}.b`>); |
| 157 | +expectType<{a: [{b?: number | null; readonly c: string}]}>({} as SetNonNullableDeep<{a: [{b?: number | null; readonly c: string | undefined}]}, 'a.0.c'>); |
0 commit comments