Skip to content

Commit 864a8bf

Browse files
authored
Fix JsonStringifyResult bug with tupple (#164)
* Fix JsonStringifyResult bug with tupple * Fix
1 parent 2117f27 commit 864a8bf

File tree

3 files changed

+32
-36
lines changed

3 files changed

+32
-36
lines changed

pkgs/typed-api-spec/src/fetch/index.t-test.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,6 @@ type ValidateUrlTestCase = [
118118
(async () => {
119119
const f = fetch as FetchT<"", JsonSpec>;
120120
const f2 = fetch as FetchT<"", Spec>;
121-
const JSONT = JSON as JSONT;
122121
{
123122
// @ts-expect-error fetch requires input
124123
f();

pkgs/typed-api-spec/src/json/index.test.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ describe("JsonStringifyResult", () => {
2020
k: (string | undefined | Date)[];
2121
[l]: string;
2222
m: { toJSON: () => { x: number; y: string | undefined } };
23+
n: [number, number];
2324
};
2425
// eslint-disable-next-line @typescript-eslint/no-unused-vars
2526
type T = Expect<
@@ -35,6 +36,7 @@ describe("JsonStringifyResult", () => {
3536
k: (string | null)[];
3637
// FIXME: y should be optional
3738
m: { x: number; y: string | undefined };
39+
n: [number, number];
3840
}
3941
>
4042
>;
@@ -55,6 +57,7 @@ describe("JsonStringifyResult", () => {
5557
k: ["a", undefined, new Date("2021-01-01")],
5658
[l]: "symbol keyed value",
5759
m: { toJSON: () => ({ x: 1, y: undefined }) },
60+
n: [1, 2],
5861
};
5962

6063
expect(JSON.parse(JSON.stringify({ ...example, h: undefined }))).toEqual({
@@ -66,6 +69,7 @@ describe("JsonStringifyResult", () => {
6669
j: { nested: "world" },
6770
k: ["a", null, "2021-01-01T00:00:00.000Z"],
6871
m: { x: 1 },
72+
n: [1, 2],
6973
});
7074
});
7175
});

pkgs/typed-api-spec/src/json/index.ts

Lines changed: 28 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -20,47 +20,40 @@ type JsonPrimitive = string | number | boolean | null | Date;
2020
// eslint-disable-next-line @typescript-eslint/ban-types
2121
type InvalidJsonValue = undefined | Function | symbol | bigint;
2222

23-
// 配列要素の変換: 不適切な値は null に
24-
type JsonifyArrayElement<T> = T extends InvalidJsonValue ? null : Jsonify<T>;
25-
26-
// オブジェクトの変換
2723
type JsonifyObject<T> = {
28-
// keyof T から string 型のキーのみを抽出 (シンボルキーを除外)
2924
[K in keyof T as K extends string
30-
? // プロパティの値 T[K] を Jsonify した結果を ProcessedValue とする
31-
Jsonify<T[K]> extends infer ProcessedValue
32-
? // ProcessedValue が 不適切な型なら、このプロパティ自体を除外 (never)
33-
ProcessedValue extends InvalidJsonValue
34-
? never
35-
: // そうでなければキー K を採用
36-
K
37-
: never
38-
: never]: Jsonify<T[K]>; // ↑で採用されたキー K に対して、変換後の値 ProcessedValue を割り当て
25+
? T[K] extends InvalidJsonValue
26+
? never
27+
: K
28+
: never]: Jsonify<T[K]>;
3929
};
4030

41-
// メインの再帰型
42-
type Jsonify<T> =
43-
// 1. toJSONメソッドを持つか? -> あればその返り値を Jsonify
44-
T extends { toJSON(): infer R }
45-
? Jsonify<R>
46-
: // 2. Dateか? -> string
47-
T extends Date
48-
? string
49-
: // 3. その他のプリミティブか? -> そのまま
50-
T extends JsonPrimitive
31+
// タプル型を保持するためのヘルパー型 - 再帰の深さを制限
32+
type JsonifyTuple<T extends readonly unknown[]> = T extends [
33+
infer First,
34+
...infer Rest,
35+
]
36+
? [
37+
First extends InvalidJsonValue ? null : Jsonify<First>,
38+
...JsonifyTuple<Rest>,
39+
]
40+
: [];
41+
42+
type Jsonify<T> = T extends { toJSON(): infer R }
43+
? Jsonify<R>
44+
: T extends Date
45+
? string
46+
: T extends JsonPrimitive
47+
? T
48+
: T extends InvalidJsonValue
5149
? T
52-
: // 4. 不適切な値か? -> そのまま (呼び出し元で処理)
53-
T extends InvalidJsonValue
54-
? T
55-
: // 5. 配列か? -> 各要素を JsonifyArrayElement で変換
56-
T extends Array<infer E>
57-
? Array<JsonifyArrayElement<E>>
58-
: // 6. オブジェクトか? -> JsonifyObject で変換
59-
T extends object
50+
: T extends readonly [unknown, ...unknown[]] // タプル型の特別処理
51+
? JsonifyTuple<T>
52+
: T extends (infer E)[]
53+
? (E extends InvalidJsonValue ? null : Jsonify<E>)[]
54+
: T extends object
6055
? JsonifyObject<T>
61-
: // 7. それ以外 (通常は到達しない) -> never
62-
never;
56+
: never;
6357

64-
// 最終的な型: トップレベルでの undefined/function/symbol/bigint は undefined になる
6558
export type JsonStringifyResult<T> =
6659
Jsonify<T> extends InvalidJsonValue ? undefined : Jsonify<T>;

0 commit comments

Comments
 (0)