TypeScript で型レベル FizzBuzz
動機と目的
TypeScript 初心者なので、FizzBuzz を書こうと思いました。
TypeScript にはリテラル型とタプル型があるので、[1, 2, "Fizz", 4, "Buzz", ...]
という FizzBuzz 型を生成することができるはずです。
型のインスタンス化の制限のため、あまり長くは生成できそうにありませんが、2 回目の "FizzBuzz"
くらいまではたどり着きたいです。
外部ライブラリは使用しません。
結果
今の私の TypeScript 力で、FizzBuzz 感を出しつつ 2 回目の "FizzBuzz"
まで生成しようとすると、こんな感じになりました。
type Buzz<N, ZS1 extends readonly any[] = [0]> =
{
0:
[]
1:
((z1: 0, ...zs1: ZS1) => any) extends ((...zs2: infer ZS2) => any) ?
((z2: 0, ...zs2: ZS2) => any) extends ((...zs3: infer ZS3) => any) ?
((z3: 0, ...zs3: ZS3) => any) extends ((...zs4: infer ZS4) => any) ?
((z4: 0, z5: 0, ...zs4: ZS4) => any) extends ((...zs6: infer ZS6) => any) ?
Buzz<N, ZS6> extends infer BS6 ?
BS6 extends readonly any[] ?
((b1: ZS1['length'], b2: ZS2['length'], b3: ZS3['length'], b4: ZS4['length'], b5: 'Buzz', ...bs6: BS6) => any) extends ((...bs1: infer BS1) => any) ?
BS1 :
never : never : never : never : never : never : never;
}[ZS1['length'] extends N ? 0 : 1];
type Fizz<BS1 extends readonly any[]> =
{
0:
[];
1:
((...bs1: BS1) => any) extends ((b1: infer B1, b2: infer B2, b3: infer B3, ...bs4: infer BS4) => any) ?
Fizz<BS4> extends infer FS4 ?
FS4 extends readonly any[] ?
((f1: B1, f2: B2, f3: B3 extends 'Buzz' ? 'FizzBuzz' : 'Fizz', ...fs4: FS4) => any) extends ((...fs1: infer FS1) => any) ?
FS1 :
never : never : never : never;
}[BS1['length'] extends 0 ? 0 : 1];
// 'N' shall be of the form '15K + 1'.
type FizzBuzz<N> = Fizz<Buzz<N>>;
const fizzBuzz: FizzBuzz<31> = 'FizzBuzz';
[email protected]
で実行しました。
% tsc fizzbuzz.ts
fizzbuzz.ts:34:7 - error TS2322: Type '"FizzBuzz"' is not assignable to type '[1
, 2, "Fizz", 4, "Buzz", "Fizz", 7, 8, "Fizz", "Buzz", 11, "Fizz", 13, 14, "FizzB
uzz", 16, 17, "Fizz", 19, "Buzz", "Fizz", 22, 23, "Fizz", "Buzz", 26, "Fizz", 28
, 29, "FizzBuzz"]'.
33 const fizzBuzz: FizzBuzz<31> = 'FizzBuzz';
~~~~~~~~
Found 1 error.
できていそうです。
解説
TypeScript のままだと読みにくいので、TypeScript に書き直します。
function buzz(n: number, zs1: readonly 0[] = [0]): readonly ('Buzz' | number)[] {
if (zs1.length === n) {
return [];
} else {
const zs2 = [0, ...zs1] as const;
const zs3 = [0, ...zs2] as const;
const zs4 = [0, ...zs3] as const;
const zs6 = [0, 0, ...zs4] as const;
const bs6 = buzz(n, zs6);
const bs1 = [zs1.length, zs2.length, zs3.length, zs4.length, 'Buzz', ...bs6] as const;
return bs1;
}
}
function fizz(bs1: readonly ('Buzz' | number)[]): readonly ('FizzBuzz' | 'Fizz' | 'Buzz' | number)[] {
if (bs1.length === 0) {
return [];
} else {
const [b1, b2, b3, ...bs4] = bs1;
const fs4 = fizz(bs4);
const fs1 = [b1, b2, b3 === 'Buzz' ? 'FizzBuzz' : 'Fizz', ...fs4] as const;
return fs1;
}
}
// 'n' shall be of the form '15k + 1'.
function fizzBuzz(n: number): readonly ('FizzBuzz' | 'Fizz' | 'Buzz' | number)[] {
return fizz(buzz(n));
}
console.log(fizzBuzz(31));
自然数列を生成する方法が肝のように思います。
自然数から直接次の自然数を得ることはできないようなので、今回は適当な配列を伸ばしていき、その length
を取ることで自然数列を作っています。
また 5 ずつまとめて自然数を生成することで、型のインスタンス化の制限を避けようとしています。ついでに "Buzz"
も埋め込んでいます。
もっと言えば 15 ずつ処理することもできるはずですが、個人的に FizzBuzz してる感が薄れるので、やっていません。
まとめ
TypeScript で FizzBuzz をやってみました。
Author And Source
この問題について(TypeScript で型レベル FizzBuzz), 我々は、より多くの情報をここで見つけました https://qiita.com/iorate/items/84836bc94ade93ab5921著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .