在网上看到一个用TypeScript的类型求解数独的代码。 但是我按照TypeScript的编译执行过程,没有任何输出。而执行app.ts编译后的js文件是有输出的。
export PATH=$PATH:/mnt/c/d/node-v24.4.1-linux-x64/bin
npm config set registry https://registry.npmmirror.com
npm install -g typescript
added 1 package in 17s
tsc -v
Version 5.9.3
cd ../..
tsc app.ts
node app.js
Hello World
tsc sudoku2.ts
于是询问DeepSeek:
怎么执行typescript程序,我对附件执行tsc sudoku2.ts得到一个sudoku2.js,但里面只有一行注释//type Answer3 = Solve;
他的解答如下:
这是因为你的 TypeScript 代码完全是在类型层面(type level)运行的,而不是在值层面。你使用的是 TypeScript 的类型系统本身来“计算”数独的解,而不是编写运行时的 JavaScript 代码。
让我们来解释一下:
你的代码做了以下事情:
如何看到结果?
由于这是类型计算,你需要通过 TypeScript 编译器来查看类型推断的结果。有几种方法:
方法1:使用编辑器/IDE
在 VSCode 或其他支持 TypeScript 的编辑器中,将鼠标悬停在 Answer1 或 Answer2 上,你会看到类型推导的结果。
方法2:创建类型检查文件
创建一个测试文件来查看结果:
// test.ts
import type { Answer1, Answer2 } from './sudoku2';
// 创建一个错误来查看类型
type ShowAnswer1 = Answer1; // 将鼠标悬停在这里看结果
type ShowAnswer2 = Answer2;
// 或者故意制造类型错误来查看
const a1: Answer1 = [];
// 错误信息会显示 Answer1 的实际类型
方法3:使用 tsc –showConfig 或创建接口
// 在你的 sudoku2.ts 文件末尾添加
interface Answers {
answer1: Answer1;
answer2: Answer2;
// answer3: Answer3; // 取消注释这个试试
}
方法4:使用 TypeScript Playground
将你的代码复制到 TypeScript Playground 中,然后将鼠标悬停在类型上查看。
注意:
- 这种类型层面的计算在编译时完成,不会生成任何 JavaScript 代码
- Answer3 被注释掉可能是因为这个数独太难,类型递归深度超过了 TypeScript 的限制(默认约 1000 层)
- 如果你想要一个运行时求解器,你需要编写真正的 JavaScript/TypeScript 函数,而不是使用类型系统
创建运行时求解器:
如果你想要一个实际的数独求解器程序,你需要编写类似这样的代码:
// runtime-solver.ts
function solveSudoku(board: number[][]): boolean {
// 实际的回溯算法实现
// …
}
const problem1 = [
[0, 2, 4, 0, 8, 7, 5, 0, 9],
// … 其他行
];
solveSudoku(problem1);
console.log(problem1);
你当前的代码是一个类型体操(type gymnastics)的练习,展示了 TypeScript 类型系统的强大,但不是用来实际运行的。
我将代码复制到 TypeScript Playground,果真点击执行后,把鼠标移动到相应类型上,它就显示了求出的数独解。如下所示
type Answer1 = [3, 2, 4, 1, 8, 7, 5, 6, 9, 8, 9, 1, 4, 5, 6, 3, 7, 2, 5, 6, 7, 2, 9, 3, 8, 4, 1, 7, 8, 6, 5, 2, 9, 1, 3, 4, 2, 5, 9, 3, 1, 4, 7, 8, 6, 1, 4, 3, 6, 7, 8, 2, 9, 5, 4, 7, 2, 9, 3, 1, 6, 5, 8, 6, 3, 5, 8, 4, 2, 9, 1, 7, 9, 1, 8, 7, 6, 5, 4, 2, 3]
type Answer2 = [6, 9, 4, 8, 5, 7, 1, 2, 3, 3, 8, 7, 1, 4, 2, 6, 5, 9, 1, 5, 2, 6, 3, 9, 7, 8, 4, 2, 3, 5, 4, 8, 1, 9, 6, 7, 4, 1, 8, 9, 7, 6, 5, 3, 2, 7, 6, 9, 3, 2, 5, 8, 4, 1, 8, 7, 6, 2, 9, 3, 4, 1, 5, 9, 2, 1, 5, 6, 4, 3, 7, 8, 5, 4, 3, 7, 1, 8, 2, 9, 6] | [6, 9, 7, 8, 4, 2, 1, 5, 3, 3, 8, 4, 1, 5, 7, 6, 2, 9, 1, 5, 2, 6, 3, 9, 7, 8, 4, 2, 3, 5, 4, 8, 1, 9, 6, 7, 4, 1, 8, 9, 7, 6, 5, 3, 2, 7, 6, 9, 3, 2, 5, 8, 4, 1, 8, 7, 6, 2, 9, 3, 4, 1, 5, 9, 2, 1, 5, 6, 4, 3, 7, 8, 5, 4, 3, 7, 1, 8, 2, 9, 6]
type Answer3 = [4, 5, 8, 3, 2, 6, 7, 1, 9, 6, 7, 2, 9, 1, 4, 3, 8, 5, 9, 1, 3, 8, 5, 7, 4, 2, 6, 3, 4, 1, 5, 7, 9, 2, 6, 8, 8, 9, 6, 2, 3, 1, 5, 7, 4, 7, 2, 5, 4, 6, 8, 1, 9, 3, 1, 3, 7, 6, 8, 5, 9, 4, 2, 5, 8, 4, 7, 9, 2, 6, 3, 1, 2, 6, 9, 1, 4, 3, 8, 5, 7]
虽然没什么实用性,也算增长了typescript方面的见识。
翻译了注释的代码如下
// 可用的数字是从1到9
type Digit = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;
// 空白格子用0表示
type Empty = 0;
// 长度为9的元组类型
type Tuple9<T> = [T, T, T, T, T, T, T, T, T];
// 长度为81的元组类型
type Tuple81<T> = [
…Tuple9<T>,
…Tuple9<T>,
…Tuple9<T>,
…Tuple9<T>,
…Tuple9<T>,
…Tuple9<T>,
…Tuple9<T>,
…Tuple9<T>,
…Tuple9<T>
];
// 表示数独问题的类型
type Problem = Tuple81<Digit | Empty>;
// 问题示例
type Problem1 = [
0, 2, 4, 0, 8, 7, 5, 0, 9,
8, 9, 1, 4, 5, 6, 3, 7, 2,
5, 6, 7, 0, 9, 3, 0, 0, 1,
7, 8, 6, 5, 2, 9, 0, 3, 4,
2, 0, 0, 3, 1, 0, 0, 0, 0,
1, 4, 0, 6, 7, 8, 0, 0, 0,
0, 0, 0, 0, 0, 0, 6, 0, 0,
0, 3, 0, 0, 0, 0, 0, 1, 0,
9, 0, 0, 0, 0, 5, 0, 0, 0,
];
// 将元组的索引转换为行号的类型
type ToRow = [
0, 0, 0, 0, 0, 0, 0, 0, 0,
1, 1, 1, 1, 1, 1, 1, 1, 1,
2, 2, 2, 2, 2, 2, 2, 2, 2,
3, 3, 3, 3, 3, 3, 3, 3, 3,
4, 4, 4, 4, 4, 4, 4, 4, 4,
5, 5, 5, 5, 5, 5, 5, 5, 5,
6, 6, 6, 6, 6, 6, 6, 6, 6,
7, 7, 7, 7, 7, 7, 7, 7, 7,
8, 8, 8, 8, 8, 8, 8, 8, 8
];
// 将元组的索引转换为列号的类型
type ToColumn = [
0, 1, 2, 3, 4, 5, 6, 7, 8,
0, 1, 2, 3, 4, 5, 6, 7, 8,
0, 1, 2, 3, 4, 5, 6, 7, 8,
0, 1, 2, 3, 4, 5, 6, 7, 8,
0, 1, 2, 3, 4, 5, 6, 7, 8,
0, 1, 2, 3, 4, 5, 6, 7, 8,
0, 1, 2, 3, 4, 5, 6, 7, 8,
0, 1, 2, 3, 4, 5, 6, 7, 8,
0, 1, 2, 3, 4, 5, 6, 7, 8
];
// 将元组的索引转换为3*3分组号的类型
type ToSquare = [
0, 0, 0, 1, 1, 1, 2, 2, 2,
0, 0, 0, 1, 1, 1, 2, 2, 2,
0, 0, 0, 1, 1, 1, 2, 2, 2,
3, 3, 3, 4, 4, 4, 5, 5, 5,
3, 3, 3, 4, 4, 4, 5, 5, 5,
3, 3, 3, 4, 4, 4, 5, 5, 5,
6, 6, 6, 7, 7, 7, 8, 8, 8,
6, 6, 6, 7, 7, 7, 8, 8, 8,
6, 6, 6, 7, 7, 7, 8, 8, 8
];
type PopValue<T extends Digit[], K extends number, V extends Digit | Empty> = {
[key in keyof T]: key extends `${K}` ? Exclude<T[key], V> : T[key];
};
type RowLimit<
Board extends Problem,
Ret extends Tuple9<Digit> = Tuple9<Digit>,
I extends unknown[] = []
> = I["length"] extends 81
? Ret
: RowLimit<
Board,
PopValue<Ret, ToRow[I["length"]], Board[I["length"]]>,
[…I, unknown]
>;
type ColumnLimit<
Board extends Problem,
Ret extends Tuple9<Digit> = Tuple9<Digit>,
I extends unknown[] = []
> = I["length"] extends 81
? Ret
: ColumnLimit<
Board,
PopValue<Ret, ToColumn[I["length"]], Board[I["length"]]>,
[…I, unknown]
>;
type SquareLimit<
Board extends Problem,
Ret extends Tuple9<Digit> = Tuple9<Digit>,
I extends unknown[] = []
> = I["length"] extends 81
? Ret
: SquareLimit<
Board,
PopValue<Ret, ToSquare[I["length"]], Board[I["length"]]>,
[…I, unknown]
>;
type Solve<
Board extends Problem,
Row extends Tuple9<Digit> = RowLimit<Board>,
Column extends Tuple9<Digit> = ColumnLimit<Board>,
Square extends Tuple9<Digit> = SquareLimit<Board>,
Answer extends Digit[] = [],
L extends number = Answer["length"],
T = Board[L] extends Empty
? Row[ToRow[L]] & Column[ToColumn[L]] & Square[ToSquare[L]]
: Board[L]
> = L extends 81
? Answer
: T extends Digit
? Solve<
Board,
PopValue<Row, ToRow[L], T>,
PopValue<Column, ToColumn[L], T>,
PopValue<Square, ToSquare[L], T>,
[…Answer, T]
>
: never;
type Answer1 = Solve<Problem1>;
type Problem2 = [
0, 0, 0, 0, 0, 0, 0, 0, 3,
0, 0, 0, 0, 0, 0, 6, 0, 9,
1, 5, 2, 0, 0, 0, 0, 0, 0,
2, 3, 0, 4, 8, 0, 0, 0, 0,
4, 1, 8, 0, 0, 6, 5, 0, 0,
7, 0, 9, 3, 2, 5, 0, 0, 0,
8, 0, 6, 0, 9, 3, 4, 1, 0,
9, 2, 1, 5, 0, 0, 3, 0, 0,
5, 0, 0, 0, 1, 8, 2, 9, 6
];
type Answer2 = Solve<Problem2>;
type Problem3 = [
0, 0, 0, 3, 0, 0, 0, 1, 9,
6, 7, 0, 0, 0, 4, 0, 0, 0,
0, 0, 0, 8, 0, 0, 0, 2, 0,
3, 4, 0, 0, 7, 0, 2, 0, 0,
0, 0, 6, 0, 3, 1, 5, 0, 0,
0, 0, 0, 0, 6, 0, 0, 9, 0,
1, 3, 0, 6, 0, 0, 0, 0, 2,
0, 0, 4, 7, 0, 0, 6, 0, 0,
2, 0, 9, 0, 4, 0, 0, 0, 0
];
//type Answer3 = Solve<Problem3>;
