類型系統

在计算机科学中，类型系统（英语：type system）用于定义如何将编程语言中的数值和表达式归类为许多不同的类型，如何操作这些类型，这些类型如何互相作用。类型可以确认一个值或者一组值具有特定的意义和目的（虽然某些类型，如抽象类型和函数类型，在程序运行中，可能不表示为值）。类型系统在各种语言之间有非常大的不同，也许，最主要的差异存在于编译时期的语法，以及运行时期的操作实现方式。
编译器可能使用值的静态类型以最优化所需的存储区，并选取对值运算时的较佳算法。例如，在许多C编译器中，“浮点数”数据类型是以32 比特表示，与IEEE 754规格一致的单精度浮点数。因此，在数值运算上，C应用了浮点数规范（浮点数加法、乘法等等）。
类型的约束程度以及评估方法，影响了语言的类型。更进一步，编程语言可能就类型多态性部分，对每一个类型都对应了一个极度个别的算法的运算。类型理论研究类型系统，尽管实际的编程语言类型系统，起源于计算机体系结构的实际问题、编译器实现，以及语言设计。

类型

：断言变量e的类型是int。

类型检查

类型检查所进行的检验处理以及实行类型的约束，可发生在编译时期（静态检查）或运行时期（动态检查）。静态类型检查是在编译器所进行语义分析中进行的。如果一个语言强制实行类型规则（即通常只允许以不丢失信息为前提的自动类型转换）就称此处理为强类型，反之称为弱类型。

静态和动态检查

如果一个编程语言的类型检查，可在不测试运行时期表达式的等价性的情况下进行，该语言即为静态类型的。一个静态类型的编程语言，是在运行时期和编译时期之间的处理阶段下重视这些区别的。如果程序的独立模块，可进行各自的类型检查（独立编译），而无须所有会在运行时出现的模块的那些信息，该语言即具有一个编译时期阶段。如果一个编程语言支持运行时期（动态）调度已标记的数据，该语言即为动态类型的。如果一个编程语言破坏了阶段的区别，因而类型检查需要测试运行时期的表达式的等价性，该语言即为依存类型的。
在动态类型中，经常在运行时期进行类型标记的检查，因为变量所约束的值，可经由运行路径获得不同的标记。在静态类型编程语言中，类型标记使用识别联合类型表示。
动态类型经常出现于脚本语言和RAD语言中。动态类型在解释型语言中极为普遍，编译语言则偏好无须运行时期标记的静态类型。对于类型和隐式类型语言较完整的列表参见类型和隐式类型语言。
术语推断类型（鸭子类型，duck typing）指的是动态类型在语言中的应用方式，它会“推断”一个数值的类型。
看看类型标记检查是如何运作的，考虑下列假码示例：
var x; //（1）
x := 5; //（2）
x := "hi"; //（3）
在这个示例中，（1）宣告x；（2）将整数值5代给x；（3）将字符串值"hi"代给x。在主要的静态系统中，这个代码片断将会违反规则，因为（2）和（3）对 x所约束的类型相矛盾。
相较之下，一个纯粹的动态类型系统允许上述程序的运行，因为类型标记附到数值上（不是变量）。在处理错误语句或表达式的时候，以动态类型实现的语言会捕捉程序的错误，而不是误用错误类型的数值。换句话说，动态类型捕捉在程序运行时的错误。
典型的动态类型实现，会以类型标记维持程序所有数值的“标记”，并在运算任何数值之前检查标记。例如：
var x := 5; //（1）var y := "hi"; //（2）var z := x + y; //（3）
在这个程序片断中，（1）将数值5约束给x；（2）将数值"hi"约束给y；以及（3）尝试将x加到y。在动态类型语言中，约束给x的值会是一对（整数, 5），且约束给y的值会是一对（字符串, "hi"）。当这个程序尝试运行第3行时，语言对类型标记整数和字符串进行检查，如果这两个类型的+（加法）运算尚未定义，就会发出一个错误。
某些静态语言有一个“后门”，在这些编程语言中，能够编写一些不被静态类型所检查的代码。例如，Java和C-风格的语言有“转型”可用。在静态类型的编程语言中，不必然意味着缺乏动态类型机制。例如Java使用静态类型，但某些运算需要支持运行时期的类型测试，这就是动态类型的一种形式。更多静态和动态类型的讨论，请参阅编程语言。

实践中的静态和动态类型检查

对静态类型和动态类型两者之间的权衡也是必要的。
静态类型在编译时期时，就能可靠地发现类型错误。因此通常能增进最终程序的可靠性。然而，有多少的类型错误发生，以及有多少比例的错误能被静态类型所捕捉，当前对此仍有争论。静态类型的拥护者认为，当程序通过类型检查时，它才有更高的可靠性。虽然动态类型的拥护者指出，实际流通的软件证明，两者在可靠性上并没有多大差别。可以认为静态类型的价值，在于增进类型系统的强化。强类型语言（如ML和Haskell）的拥护者提出，几乎所有的bug都可以看作是类型错误，如果编写者以足够恰当的方式，或者由编译器推断来宣告一个类型。
静态类型通常可以编译出速度较快的代码。当编译器清楚知道所要使用的数据类型，就可以产生最优化过后的机器代码。更进一步，静态类型语言中的编译器，可以更轻易地发现较佳快捷方式。某些动态语言（如Common Lisp）允许任意类型的宣告，以便于最优化。以上理由使静态类型更为普及。参阅最优化。
相较之下，动态类型允许编译器和解译器更快速的运作。因为源代码在动态类型语言中，变更为减少进行检查，并减少解析代码。这也可减少编辑－编译－测试－调试的周期。
静态类型语言缺少类型推断（如Java），而需要编写者宣告所要使用的方法或函数的类型。编译器将不允许编写者忽略，这可为程序起附加性帮助文档的作用。但静态类型语言也可以无须类型宣告，所以与其说是静态类型的代价，倒不如说是类型宣告的报酬。
静态类型允许构造函数库，它们的用户不太可能意外的误用。这可作为传达库开发者意图的额外机制。
动态类型允许建构一些静态类型系统所做不出来的东西。例如，eval函数，它使得运行任意数据作为代码成为可能（不过其代码的类型仍是静态的）。此外，动态类型容纳过渡代码和原型设计，如允许使用字符串代替数据结构。静态类型语言最近的增强（如Haskell 一般化代数数据类型）允许eval函数以类型安全的方式撰写。
动态类型使元编程更为强大，且更易于使用。例如C++模板的写法，比起等价的Ruby或Python写法要来的麻烦。更高度的运行时期构成物，如元类别（metaclass）和内观（Introspection），对静态类型语言而言通常更为困难。

强类型和弱类型

强类型的基本定义即为，禁止错误类型的参数继续运算。C语言的类型转换即为缺乏强类型的证例；如果编写者用C语言对一个值转换类型，不仅令编译器允许这个代码，而且在运行时期中也同样允许。这使得C代码可更为紧密和快速，不过也使调试变的更为困难。
部分学者使用术语存储器安全语言（或简称为安全语言）形容禁止未定义运算发生的语言。例如，某个存储器安全语言将会检查数组边界。
弱类型意指一个语言可以隐式的转换类型（或直接转型）。看看先前的例子：
var x := 5;
var y := "37";
x + y;
在弱类型语言中编写上述代码，并不清楚将会得到哪一种结果。某些语言如Visual Basic，将会产生可以运作的代码，它将会给出的结果是42：系统将字符串"37"转换成数字37，以符合运算上的直觉；其它的语言，像JavaScript将会产生的结果是"537"：系统将数字5转换成字符串"5"并把两者串接起来。在Visual Basic和JavaScript中，最终的类型是以那两个操作数为考量的规则所决定。在部分语言中，如AppleScript，某个值最终的类型，只以最左边的操作数的类型所决定。
设计精巧的语言也允许语言显现出弱类型（借由类型推断之类的技术）的特性以方便使用，并且保留了强类型语言所提供的类型检查和保护。例子包括VB.Net、C#以及Java。
运算符重载所带来的简化，像是不以算术运算中的加法来使用“+”，可以减少一些由动态类型所造成的混乱。例如，部分语言使用“.”或“&”来串连字符串。

类型系统的安全性

编程语言的类型系统的第三种分类方法，就是类型运算和转换的安全性。如果它不允许导致不正确的情况的运算或转换，计算机科学就认为该语言是“类型安全”的。
再次看看这个假码例子：
var x := 5;
var y := "37";
var z := x + y;
在一个如Visual Basic的语言中，例子中的变量z得到的值为42。不管编写者有没有这个意图，该语言定义了明确的结果，且程序不会就此崩溃，或将不明定义的值赋给z。就这方面而言，这样的语言就是类型安全的。
现在来看C的相同例子：
int x = 5;
char y[] = "37";
char* z = x + y;
在这个例子中，z将会指向一个超过y地址5个字节的存储器地址，相当于指向y字符串的指针之后的两个空字符之处。这个地址的内容尚未定义，且有可能超出存储器的定址界线，而且就这么引用参考z会引起程序的终止。虽是一个良好类型，但却不是存储器安全的程序——如果以对类型安全语言而言不该发生为先决条件的话。

多态性和类型

术语“多态性”指的是：代码（尤其是函数和类别）对各种类型的值能够动作，或是相同数据结构的不同实体能够控制不同类型的元素。为了提升复用代码的潜在价值，类型系统逐渐允许多态性：在具有多态性的语言中，程序设计者只需要实现如列表或词典的数据结构一次，而不是对使用到它的元素的每一个类型都规划一次。基于这个原因，计算机学家也称使用了一定的多态性的方法为泛型程序设计。类型理论的多态性基础与抽象化、模块化和（偶尔）子类型有相当密切的联系关系。

显示宣告和隐式暗示

许多静态类型系统，如C和Java，要求要宣告类型：编写者必须以指定类型明确地关系到每一个变量上。其它的，如Haskell，则进行类型推断：编译器根据编写者如何运用这些变量，以草拟出关于这个变量的类型的结论。例如，给定一个函数f（x,y），它将x和y加起来，编译器可以推断出x和y必须是数字——因为加法仅定义给数字。因此，任何在其它地方以非数值类型（如字符串或链表）作为参数来调用f的话，将会发出一个错误。
在代码中数值、字符串常量以及表达式，经常可以在详细的前后文中暗示类型。例如，一个表达式3.14可暗示浮点数类型；而[1, 2, 3]则可暗示一个整数的链表；通常是一个数组。

兼容性：等价性和子类型

对于静态类型语言的类型检查器，必须检验所有表达式的类型，是否与前后文所期望的类型一致。例如指派语句x := e，推断表达式e的类型，必定与宣告或推断的变量类型x一致。这个一致性的概念，就称为兼容性，是每一个编程语言所特有的。
很明显，如果e和x的类型相同，就允许指派，然后这是一个有效的表达式。因此在最简单的类型系统中，问题从两个类型是否兼容，简化为两个类型是否相等（或等价）。然而不同的语言对于两个类型表达式是否理解为表示了相同类型，有着不同的标准。类型的相等理论的差异相当巨大，两个极端的例子是结构类型系统（Structural type system），任两个以相同结构所描述的值的类型都是等价的，且在标明类型系统（Nominative type system）上，没有两个独特的语法构成的类型表达式表示同一类型，（即类型若要相等，就必须具有相同的“名字”）。
在子类型的语言中，兼容关系更加复杂。特别是如果A是B的子类型，那么类型A的值可用于类型B也属意料之中，但反过来就不是这样。如同等价性，对每一个编程语言而言，子类型的关系的定义是不同的，可能存在各种变化。在语言中出现的参数或者特定的多态性，也可能意味着具有对类型的兼容性。

争议

在强类型、静态类型语言的支持者，和动态类型、自由形式的支持者之间，经常发生争执。前者主张，在编译的时候就可以较早发现错误，而且还可增进运行时期的性能。后者主张，使用更加动态的类型系统，分析代码更为简单，减少出错机会，才能更加轻松快速的编写程序。与此相关的是，考虑到在类型推断的编程语言中，通常不需要手动宣告类型，这部分的额外开销也就自动降低了。