除TypeScript外,是否有其他编程语言支持映射类型与条件类型特性?
Great question! TypeScript's mapped and conditional types are indeed standout features for crafting intuitive, developer-friendly APIs—especially when working with JavaScript's object-oriented patterns. While they feel uniquely tailored to TS's ecosystem, several other languages offer comparable (if not more powerful) type-level programming capabilities. Let’s dive into them:
Languages with Similar Type-Level Features
1. Flow (Facebook's Type Checker)
As a close cousin to TypeScript, Flow includes direct equivalents for both mapped and conditional types:
- Mapped types: Use
$ObjMapto transform every value type in an object:type Optionalize<T> = $ObjMap<T, <V>(v: V) => ?V>; type User = { id: number; name: string }; type OptionalUser = Optionalize<User>; // { id: ?number; name: ?string } - Conditional types: The
$Conditionalutility lets you branch based on type checks:type IsString<T> = $Conditional<T, string, true, false>; type Test1 = IsString<string>; // true type Test2 = IsString<number>; // false
2. Rust
Rust’s strong static type system, combined with traits and type families, can mimic the behavior of TS’s mapped and conditional types:
- Mapped types: You can use custom derive macros or type-level functions to transform struct field types. For example, a macro that wraps every field type in
Option:#[derive(Debug)] struct User { id: u32, name: String } // A custom derive could generate: #[derive(Debug)] struct OptionalUser { id: Option<u32>, name: Option<String> } - Conditional types: Rust uses trait bounds and associated type families to create conditional logic at the type level. For example:
trait MaybeOptional<T> { type Output; } impl<T: Copy> MaybeOptional<T> for T { type Output = T; } impl<T> MaybeOptional<T> for T { type Output = Option<T>; } type TestCopy = <u32 as MaybeOptional<u32>>::Output; // u32 type TestNonCopy = <String as MaybeOptional<String>>::Output; // Option<String>
3. Scala (Scala 3+)
Scala 3’s advanced type system introduces match types (conditional types) and built-in utilities for mapping over types:
- Mapped types: The
Tuple.Maputility transforms every element in a tuple, similar to TS’s mapped object types:type OptionalTuple[T <: Tuple] = Tuple.Map[T, Option] type UserTuple = (Int, String) type OptionalUserTuple = OptionalTuple[UserTuple] // (Option[Int], Option[String]) - Conditional types: Match types let you pattern-match on type structures, just like TS’s conditional types:
type IsString[T] = T match { case String => true case _ => false } type Test1 = IsString[String] // Boolean = true type Test2 = IsString[Int] // Boolean = false
4. Haskell
Haskell uses type families and multi-parameter type classes to achieve type-level mapping and conditional logic:
- Mapped types: Type families can recursively transform type structures, like mapping
Maybeover a tuple:type family MapMaybe a where MapMaybe (a, b) = (MapMaybe a, MapMaybe b) MapMaybe Int = Maybe Int MapMaybe String = Maybe String type UserTuple = (Int, String) type OptionalUserTuple = MapMaybe UserTuple -- (Maybe Int, Maybe String) - Conditional types: Closed type families let you define conditional branches based on input types:
type family IsString a where IsString String = 'True IsString a = 'False type Test1 = IsString String -- 'True type Test2 = IsString Int -- 'False
Key Distinction for TypeScript
While these languages offer comparable type-level capabilities, TypeScript’s mapped and conditional types feel uniquely approachable for JavaScript developers. They integrate seamlessly with JS’s object model, using syntax that mirrors how you’d write runtime object transformations—making it easier to transition between runtime and type-level code.
内容的提问来源于stack exchange,提问作者Sebastian Nemeth

