TypeScript类型级别与值级别编程详解及实战挑战
本文详细解析了TypeScript中值级别与类型级别编程的区别。值级代码负责运行时逻辑,而类型级代码用于编译期静态检查。文章介绍了类型级TypeScript作为纯函数语言的核心特性,包括泛型、条件分支、递归等,并通过具体的代码挑战(如实现泛型函数、安全头部提取等)帮助读者掌握类型级编程技巧。
区分值级别编程与类型级别编程
首先,我们需要明确值级别编程和类型级别编程之间的关键区别。
- 值级别编程旨在编写实际在生产环境中运行的代码,即在运行时执行,并为用户提供实际的功能价值。
- 类型级别编程则致力于在代码发布前(即编译阶段)确保代码不包含错误,这些类型检查在运行时会被完全移除,不产生任何运行时代价。
由于JavaScript本身没有类型系统,因此所有的JavaScript代码都属于值级别编程的范畴:
// A simple Javascript function:
function sum(a, b) {
return a + b;
}
TypeScript允许我们在JavaScript代码中添加类型注释,从而确保我们编写的sum函数只能被数字类型的参数调用,而不会被其他类型的参数错误调用:
// Using type annotations:
function sum(a: number, b: number): number {
return a + b;
}
然而,TypeScript的类型系统远比简单的类型注释强大得多。在实际开发中,我们编写的代码有时需要具备通用性,能够接受我们在编写代码时并不知晓具体类型的参数。
在这种情况下,我们可以在尖括号<A, B, ...>中定义类型参数。随后,我们可以将这些类型参数传递给一个类型级函数,该函数会根据输入的类型计算出输出的类型:
// Using type level programming:
function genericFunction<A, B>(a: A, b: B): DoSomething<A, B> {
return doSomething(a, b);
}
这就是类型级编程的核心概念!DoSomething<A,B> 是一种使用专门的语言编写的类型级函数,它与用于处理值的语言不同,但具备同样强大的表达能力。我们可以将这种专门用于类型的语言称为“类型级TypeScript”。
// This is a type-level function:
type DoSomething<A, B> = ...
// This is a value-level function:
const doSomething = (a, b) => ...
类型级编程基础
类型级TypeScript本质上是一种极简的纯函数式语言。
在类型层面,函数被称为“泛型类型”:它们接受一个或多个类型参数,并返回单个输出类型。下面是一个简单的函数示例,它接收两个类型参数并将它们包装在一个元组中:
type SomeFunction<A, B> = [A, B];
/* ---- ------
^ \
type return type
parameters
\-------------------------/
^
Generic
*/
类型级别的TypeScript功能相对精简,毕竟它的核心设计目的是为你的代码提供类型约束。尽管如此,它确实拥有足够的特性来实现(几乎)图灵完备,这意味着你可以利用它解决任意复杂度的问题。
- 代码分支:根据条件执行不同的代码路径(这相当于值级别中的if/else关键字)。
- 变量赋值:声明一个变量并在表达式中使用它(这相当于值级别中的var/let关键字)。
- 函数:可复用的逻辑单元,如我们在前面的示例中看到的泛型定义。
- 循环:通常通过递归的方式来实现迭代逻辑。
- 相等检查:使用==符号,但这里的比较对象是类型而非值。
- 此外还有更多特性等待探索!
以上就是我们将要在后续章节中学习的类型级语言的简要概述。现在,让我们开始第一次实战挑战吧!
挑战机制解析
在每一章的末尾,你将面临一些需要解决的挑战,旨在将新学到的技能付诸实践。这些挑战的代码结构如下所示:
namespace challenge {
// 1. implement a generic to get the union
// of all keys in an object type.
type GetAllKeys<Obj> = TODO;
type res1 = GetAllKeys<{ a: number }>;
type test1 = Expect<Equal<res1, "a">>;
}
namespace是一个鲜为人知的TypeScript特性,它允许我们在专用的命名空间范围内隔离每个挑战,避免污染全局类型环境。TODO是占位符代码。这是你需要替换为实际实现的部分!res1=。。。是泛型针对某些输入类型所返回的具体类型。你可以将鼠标悬停在此处以检查其当前的推导结果。type test1=Expect<Equal<res1,…>>是类型级的单元测试。它用于判断TODO部分的代码实现是否正确符合预期。
在开始挑战之前,你需要先定义好Expect和Equal这两个辅助类型工具。
type Expect<T extends true> = T;
type Equal<X, Y> = (<T>() => T extends { [k in keyof X]: X[k]; } ? 1 : 2) extends <T>() => T extends { [k in keyof Y]: Y[k]; } ? 1 : 2 ? true : false;
实战挑战
准备好迎接你的第一个挑战了吗?让我们开始:
/**
* 1. The `identity` function takes a value of any type
* and returns it. Make it generic!
*/
namespace genericFunction {
function identity(a: TODO): TODO {
return a;
}
let input1 = 10;
let res1 = identity(input1);
type test1 = Expect<Equal<typeof res1, number>>;
let input2 = "Hello";
let res2 = identity(input2);
type test2 = Expect<Equal<typeof res2, string>>;
}
/**
* 2. `safeHead` takes an array, a default value
and returns the first element of the array
if it isn't empty. Make it generic!
*/
namespace safeHead {
function safeHead(array: TODO[], defaultValue: TODO): TODO {
return array[0] ?? defaultValue;
}
let input1 = [1, 2, 3];
let res1 = safeHead(input1, 0);
type test1 = Expect<Equal<typeof res1, number>>;
let input2 = ["Hello", "Hola", "Bonjour"];
let res2 = safeHead(input2, "Hi");
type test2 = Expect<Equal<typeof res2, string>>;
}
/**
* 3. `map` transforms all values in an array to a value of
* different type. Make it generic!
*/
namespace map {
function map(array: TODO[], fn: (value: TODO) => TODO): TODO[] {
return array.map(fn);
}
let input1 = [1, 2, 3];
let res1 = map(input1, value => value.toString());
type test1 = Expect<Equal<typeof res1, string[]>>;
let input2 = ["Hello", "Hola", "Bonjour"];
let res2 = map(input2, str => str.length);
type test2 = Expect<Equal<typeof res2, number[]>>;
}
/**
* 4. `pipe2` takes a value and pipes it into 2 functions
* sequentially. For example, `pipe2(x, f1, f2)` will
* result in `f2(f1(x))`. Make it generic!
*
*/
namespace pipe2 {
function pipe2(
x: TODO,
f1: (value: TODO) => TODO,
f2: (value: TODO) => TODO
): TODO {
return f2(f1(x));
}
let res1 = pipe2(
[1, 2, 3],
arr => arr.length,
length => `length: ${length}`
);
type test1 = Expect<Equal<typeof res1, string>>;
let res2 = pipe2(
{ name: 'Alice' },
user => user.name,
name => name.length > 5
);
type test2 = Expect<Equal<typeof res2, boolean>>;
}以上就是TypeScript类型级别和值级别示例详解的详细内容,更多关于TypeScript类型级别值级别的资料请关注本站其它相关文章!
Object.assign合并JS对象的用法与注意事项
2026-11-05 17:56:08
JS大数值转字符串出现科学计数法怎么办
2026-11-05 17:09:21
JS split切割后出现空字符串怎么过滤
2026-11-05 16:00:30
在 React 或 Vue 项目中封装使用 JS 节流防抖
2026-11-05 15:27:29
JavaScript中怎么将带小数的字符串转为整数?
2026-11-05 14:32:38








