# proposal-static-type-constraints-features **Repository Path**: achun/proposal-static-type-constraints-features ## Basic Information - **Project Name**: proposal-static-type-constraints-features - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2018-06-04 - **Last Updated**: 2024-12-10 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Static type constraints WIP ECMAScript 静态类型表达式(非标准). 首先, 这里给出的方法不是银弹, 毕竟 ECMAScript 是非静态类型语言. 目标: 1. 使用合法的 ECMAScript 语法实现无副作用的静态类型描述 1. 配合额外的工具实现静态类型检查 方案: ```text 在变量初始化, 形参缺省值, 函数主体的第一句位置加入静态类型表达式. 用 `||` 运算符连接默认值, 这是无冲突的. 该表达式是 `void` 一元表达式的算子, 被称作 `TypeExpression`. ``` 位置: `TypeExpression` 位于 1. 新对象初始化值, 这意味着位于 `const`, `let`, `var` 语句 1. 形参声明缺省值, 这意味着位于函数或方法声明 1. 类属性初始化值, 在构造函数中初始化属性时声明类型 1. 执行体首条代码, 用于描述函数或方法的剩余参数和返回值类型 形式: 参见 [TypeExpression](#typeexpression) 部分 赞美 `void`, 这是无副作用的, 即便被引擎计算: ```js let v = 0, // v 有值, 缺乏类型约束 o = void 'string' || '',// o 有值, 类型为 string i = void 'number' || 0, // i 有值, 类型为 number d = void 'boolean'|| 0, // d 有值, 类型为 boolean x = void 'int'; // x 无值, 类型为 int ``` ## StaticSemantics void 类型表达式属于 Type-Annotation 原生 ECMAScript 语法解决方案, 用于静态类型(源码)检查, 而非运行期类型检查. ### assert 类型表达式的形式有严格限制, 合法性断言: 1. 已知类型 基本类型 | 衍生类型 1. 标识符 可以是: 类名 | 函数名 | 接口名, 包括引入外部 module.Identifier 1. 多维数组 叶子元素为: 标识符 | 基本类型 | 衍生类型 1. 扁平对象 属性值的类型描述为: 标识符 | 基本类型 | 衍生类型 | 多维数组 1. 其它非法 抛出错误 ### StripTypeExpression 由于在运行期被引擎计算有损性能, 移除它可改善该性能. ## TypeExpression 类型表达式的结构用 ECMAScript 语法进行自描述. ```js const Position = void (null, { line: 'number', // >= 1 column: 'number' // >= 0 }); const SourceLocation = void (null, { source: 'string' | null, start: Position, end: Position }); const ParseNode = void (null, { type: 'string', loc: SourceLocation | null }); const TypeExpression = void (ParseNode, { // 继承 ParseNode 接口 type: !'TypeExpression', // 缺省值短格式只适用于基本类型 inferred: 'boolean' | null, // 是否由分析工具推导出的类型 binding: // 具体描述 'Primitive' | 'Derivative' | 'Reference' | 'List' | 'Trait' | 'Enum' }); ``` 上例采用字符串表示标识符, 以便摆脱标识符的次序相关性, 但最终标识符必须被确定. 规则: - 缺省值短格式 `!` 可推导的基本类型包括: string | boolean | number TypeExpression 位于下列 AST 节点中的 typeAnnotation: 不完全描述 ```js const Identifier = void (null, { typeAnnotation: TypeExpression | null }); const MemberDeclaration = void (null, { typeAnnotation: TypeExpression | null }); const FunctionExpression = void (null, { returnType: TypeExpression | null }); const FormalParameter = void (null, { typeAnnotation: TypeExpression | null }); ``` ## Trait 特征类型位于 `const` 声明中, 以对象的属性特征判定类型, 不依赖 prototype. ```js const Literal = void (ParseNode, { value: 'string' | 'number' | 'boolean' | null }), Trait = void (ParseNode, { type: !'Trait', binding: 'string', // IdentifierName extends: 'Reference', // IdentifierReference | ImportedReference properties: ['TraitPattern'] }), TraitPattern = void (ParseNode, { type: !'TraitPattern', binding: 'Expression', // 省略 Expression 的定义 declaration: 'Primitive' | 'Derivative' | 'Reference' | 'List', default: Literal | Reference | null, shorthand: 'boolean' | null // 使用了 '!' 短格式 }) ; ``` 规则: - Trait 只能继承另一个 Trait - Trait 不能有缺省值 ```js const IllegalExample = void('TYPE', { prop: 'Prop' }) || {}; // throw SyntaxError: Unexpected operator ``` ## Reference 引用指向一个已存在标识符. ```js const Reference = void (ParseNode, { type: !'Reference', imported: Identifier || null, // 如果有, 必须是指向 ImportedBinding 标识符 binding: Identifier }); ``` ## Enum 枚举位于 `const` 声明中, 目的是为了方便复用. ```js const Enum = void (ParseNode, { type: !'Enum', kind: Literal, // The value must be 'type' | 'list', enum: [Reference | Literal] }); // form const EnumType = void ('type', Identifier | Literal); const EnumList = void ('list', Identifier | Literal); ``` 因为类型名也可以作为值枚举, 所以使用单词 `list` 而不是 `value`. 规则: - `void ('type', ...)` 类型枚举只用于描述类型 - `void ('list', ...)` 值枚举只用于描述缺省值 - `Literal` 必须是: string | number | boolean - `EnumerationA | EnumerationB` 是并集运算 - `undefined` 和 `null` 不能出现在枚举中 ```js const IllegalExample = void('TYPE', { prop: EnumList // throw TypeError: Expected a type }); const IllegalEnum = void ( 'non-type-list', // throw SyntaxError: Invalid or unexpected token Identifier | Literal ); const IllegalEnumType = void ('type', null | 'undefined' // throw SyntaxError: Invalid or unexpected token ); const IllegalEnumTypeA = void ('type', 'null' | 'undefined' // throw SyntaxError: Invalid or unexpected token ); const IllegalEnumValues = void ('enum', 'null' | undefined // throw SyntaxError: Invalid or unexpected token ); const IllegalObject = void ('enum', {} | [] // throw SyntaxError: Invalid or unexpected token ); const LegalEnum = void ('enum', 'null' | 'undefined' // Just a string ); ``` ## Primitive 基本类型是固定的, 不受重新声明影响且不能被继承. 参见 [primitive-value]. ```js const PrimitiveNames = void ('list', 'string' | 'number' | 'boolean' | 'symbol' | 'null' | 'undefined' ), Primitive = void (ParseNode, { type: !'Primitive', name: 'string' || PrimitiveNames // This example uses enum }) ; ``` 基本类型在类型表达式中的语义: - `'symbol'` symbol 类型 - `'string'` string 类型 - `'number'` number 类型 - `'boolean'` boolean 类型 - `'null'` 表示无值, 比如无返回值的函数 - `'undefined'` 表示未明确的, 或有待明确的, 这是该词的原语义 ## Derivative 衍生类型是固定的, 不受重新声明影响且不能被继承. 部分命名源自 [WebAssembly types]. ```js const DerivativeNames = void ('list', 'object' | 'array' | 'this' | 'function' | 'int' | 'uint' | 'i8' | 'i16' | 'i32' | 'i64' | 'i128' | 'u8' | 'u16' | 'u32' | 'u64' | 'u128' | 'f32' | 'f64' | 'f128' ), Derivative = void (ParseNode, { type: !'Derivative', name: 'string' || DerivativeNames }); ``` 其中: - `object` 判定算法 `Object.prototype.toString.call(x) === '[object Object]'` - `array` 判定算法 `Array.isArray(x) === true` - `function` 判定算法 `typeof x === 'function'` - `this` 代指所属的函数或类型 ## List 列表类型由一对方括号 `[`,`]` 包裹. ```js const List = void (ParseNode, { type: !'List', items: [ 'Primitive' | 'Derivative' | 'Reference' | 'List' ], size: Literal | null // number Literal only }); ``` ## Usage 例: ```js import mod from 'paths'; function CustomNumber( x = void (0, mod.BigNumber) // 类型为 int 或 mod.BigNumber, 缺省值为 undefined ) { void ''; // 无返回值或返回值类型为 string } class CustomClass { // 常规类, 这是最常见的类声明, 可以描述属性和方法的类型 constructor() { void { name: !'', age: !0, email: '' }; // 描述拥有的字段的类型 // 本语法表示这些字段是 public 的. // 注意这和某些在 class 中声明字段的语法在语义上完全不同. // 注意本例中 email 属性是可选的 } mustBeMethod() { // 描述必须拥有的方法 } } const PropertiesDeclaration = void { name: !'', // 必须是 string 类型 age: !0, // 必须是 int 类型 email: '' // string 类型, 允许(不存在)值为 undefined }; // 属性约束, 比常规类更宽松的类型约束声明 // // 典型场景是描述外部模块的类型, 而不和外来模块强耦合. class GenericClassDeclaration { // 泛型类, 必须被显示继承, 向使用普遍类那样使用, 没有主体代码的方法必须被覆盖 // 用泛型这个词, 是因为形式 `void undefined` 总是允许继承者重新定义类型 constructor() { void undefined; // 泛型类的判定条件, 固定写法 // 因为只有类型声明, 这个方法必须被覆盖 } any () { void undefined; // 因为只有类型声明, 这个方法必须被覆盖, 但返回什么类型由继承者决定 } } class Class extends GenericClassDeclaration { // 常规类继承泛型类的例子 any () { void ''; // 明确了类型为 string } } // @Flow type MetadataType = { [string]: any }; class MetadataType { constructor() { void {['']: undefined}; // 等价写法: void {} } } // Conflict-free compatibility writing function noConflict( x = void ''||'' // 字符串类型, 且设置了默认值, 注意默认值表达式是 ECMAScript 原有的规范. ) { void { noConflict }; // 等同于: void { noConflict: noConflict }; } // Inherit function webComponents( x = void +HTMLElement // 表示 x instanceof HTMLElement ) {} function forEach( callback = void protoCallback // 因为 protoCallback.prototype 没有更多的属性, 所以这里表示函数类型 // 判断函数类型的算法: // Object.getOwnPropertyNames(fn.prototype).join(',') === 'constructor' ) {} function protoCallback( currentValue = void undefined, index = void 0, array = void [] ) {} function mustBeResults() { void (0,''); // 返回类型必须为 int 或 string } let custom = void 0, one = void 0||1, multi = void ('', 0), obj = void CustomClass||{}; ``` 例: ```js function geometricMean(start = void 'int', end = void 'int') { void 'int'; return exp(logSum(start, end) / (end - start)); } geometricMean('0','1'); // Throws TypeError: .... geometricMean(0); // Throws TypeError: 2 argument required, but ... geometricMean(0,1,2); // Throws TypeError: 2 argument required, but ... geometricMean(0,1); // Ok ... ``` 例: ```js import asm from 'asm'; // 假设此模块描述了类型 function geometricMean(start = void asm.int, end = void asm.int) { void asm.int; return exp(logSum(start, end) / (end - start)); } geometricMean('0','1'); // Throws TypeError: .... geometricMean(0); // Throws TypeError: 2 argument required, but ... geometricMean(0,1,2); // Throws TypeError: 2 argument required, but ... geometricMean(0,1); // Ok ... ``` 如果使用者没有 import 相关模块, 具体的写法将影响代码是否会抛出错误. 出于兼容性考虑, 可以用引号包裹标识符(`quoting`), 即便标识符未定义也不会出错. 例: 这和上面的写法在语义上是等价的, 但任何时候都不会抛出错误. ```js function geometricMean(start = void 'asm.int', end = void 'asm.int') { void 'asm.int'; return exp(logSum(start, end) / (end - start)); } ``` ## License MIT License Copyright (c) 2018 YU HengChun Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. [primitive-value]: https://tc39.github.io/ecma262/#sec-primitive-value [WebAssembly types]: https://webassembly.github.io/spec/core/appendix/index-types.html