TL;DR
型の名前や明示的な宣言で、型の互換性を決める方式。構造が同じでも、宣言が別の型を名指ししていれば互換にならない。構造的型付けとは逆の考え方である。
なぜ必要か
Effective TypeScript 項目64 によると、名前的型付けを使うと、意味は違うが構造は同じ型を区別できる。たとえばユーザ ID とただの文字列を、別の型として扱える。
仕組み
Wikipedia によると、名前的な型システムでは、2 つの変数の宣言が同じ型の名前を使うときだけ互換になる。部分型の関係も、定義の中で明示的に宣言したときだけ成り立つ。たまたま構造が互換でも、宣言がなければ部分型にならない。C++、C#、Java、Kotlin、Rust などは、主に名前的な型付けと部分型を使う。
TypeScript の公式ハンドブックによると、TypeScript の型の互換性は構造的な部分型に基づく。次のコードで Dog は Pet を実装すると宣言していないが、メンバが合うので代入できる。C# や Java のような名前的型付けの言語では、同じコードはエラーになる。
interface Pet {
name: string
}
class Dog {
name: string
}
let pet: Pet
pet = new Dog() // 構造的型付けなので OK例
TypeScript で名前的型付けを実現するには、Branded Types を使う。
type UserID = string & { _brand: "user_id" }項目72 によると、文字列の enum も名前的に型付けされる。