类型的声明与转换
Alia 为静态强类型语言,类型检查在 Check 阶段执行。type 用于描述类型,typify 用于从已有对象生成类型,class 在定义对象的同时完成类型声明,as 用于显式类型转换,无函数体的 alias 声明用于向语法感知与编译器注册签名。
1. type:定义类型描述
基本语法
type <Key typeName> <Object typeDescriptionObject>
示例:
type Person *[name: String, age: Number]
type NumberOrString Number | String
type NumberArray [Number]
类型描述写法
| 写法 | 说明 | 示例 |
|---|---|---|
A | B | 联合类型 | type NumberOrString Number | String |
[T] | 数组类型 | type NumberArray [Number] |
*[k1: T1, k2: T2] | 对象类型 | type Person *[name: String, age: Number] |
[T, ...] | 可变数组 | type DynamicArray [Number, ...] |
*[k: T, ...] | 可变对象 | type DynamicObject *[name: String, ...] |
T? | 可空类型 | alias greet <String? name> -> { name } |
类型声明后可直接用于变量注解、别名参数与返回类型:
type Person *[name: String, age: Number]
const p: Person = *[name: 'Alice', age: 30]
print p.name
复合类型描述也可直接书写在别名形参位置,无需先命名:
alias sum <[Number, ...] nums> -> Number {
let total = 0
each v in nums { total += v }
return total
}
alias getName <*[name: String] person> -> String { return person.name }
重复定义同名类型报 TYPE_ALREADY_DEFINED。
2. typify:从现有对象创建类型
基本语法
typify <Key typeName> <Object origObject>
示例:
const obj = *[name: 'Alice', age: 30]
typify Person obj
typify MutablePerson *[name: 'Alice', age: 30, ...]
语义
typify按源对象各属性的推断类型生成对象描述,并以typeName注册为新类型。- 源对象尾部
...创建可变(变长)类型,允许属性增减;缺省则结构固定。 - 源对象非对象(如标量)时报
TYPE_INVALID_OPERATION。 - 重复定义同名类型报
TYPE_ALREADY_DEFINED。
const obj = *[name: 'Alice', age: 30]
typify Person obj
const p: Person = *[name: 'Bob', age: 25]
print p.name
3. class 的类型声明机制
class 声明类等同于依次执行三步:定义对象、执行 typify、以类名声明常量。
class Person *[name: '', age: 0]
等同于:
const Person = *[name: '', age: 0]
typify Person Person
带继承的类先求值继承表达式,再执行后两步:
class Student extends Person *[studentId: '']
等同于:
const Student = *[studentId: ''] extends Person
typify Student Student
规则:
- 类可变性由类体尾部
...声明(无mut修饰符)。 - 类体缺失
new时,实例化为类体对象的副本。 - 匿名类
class *[body]省略类名,仅作为表达式求值为类体对象,不注册类型与常量。
const Person = *[name: '', age: 0]
typify Person Person
const p: Person = *[name: 'Alia', age: 1]
print p.name
4. as:显式类型转换
基本语法
<Value anyValue> as <Object typeObj>
示例:
const num = '42' as Number
const str = 42 as String
const bool = 'true' as Boolean
const big = '99999999999999999999' as BigInt
转换机制
按以下顺序解析,命中即返回:
- 值与目标同类型时直接返回原值。
- 调用值的
to<目标类型>方法,如'42'.toNumber。 - 调用目标类型的
from<源类型>方法。 - 以上皆无则抛
RUNTIME_TYPE_ERROR。
const s = 'hello'
print call s.toArray
print s.length
as 亦是 <Value> 的收窄手段之一,与 typeOf、Ok 配合可解除 TYPE_VALUE_OPERATION 限制:
alias process <Value data> -> { (data as Number) * 2 }
5. 无函数体 alias:仅声明签名
基本语法
alias <Keys pattern>
alias <Keys pattern> -> <Object type>
省略栈体即为无函数体声明,仅注册语法匹配模式与参数签名,不产生运行时实现。
alias greet <String name>
alias greet <String name> -> String
语义
- 解析器注册该模式,检查器记录参数与返回类型并跳过体检查,格式化器省略栈体渲染。
- 该声明可被语法感知收集,用于补全、跳转与悬停提示。
- 编译器可识别该签名,实际实现由
@compileAlias或@compileClass在覆写端提供。 - 以语法式或
call调用该声明时,运行时抛错,提示该别名无运行时动作。 - 无体声明可携带
-> <Type>返回类型,检查器直接采用该类型,不做缺失返回与未使用参数告警。
alias greet <String name>
@compileAlias greet <String name> -> js { 'console.log(' .. name .. ')' }
完整声明与无体声明的区别:
alias add <Number a> and <Number b> -> Number { a + b }
alias remoteAdd <Number a> and <Number b> -> Number
前者解释与编译均可用,后者仅提供签名,等待覆写端实现。
声明文件 .d.alia
.d.alia 文件只承载签名声明(type 描述 + 无体 alias 方法成员 + export 接口标记),加载时注册签名但不参与运行:
type StatInfo *[
size: Number,
isFile: Boolean,
isDirectory: Boolean,
modified: String
]
type myfs *[
read: alias <String path> -> String,
stat: alias <String path> -> StatInfo,
exist: alias <String path> -> Boolean
]
export myfs
- 内置类型签名位于
Base/Core/src/types/*.d.alia,标准库签名位于Base/Core/libs/*/*.d.alia,由Base/Core/lib.alia先行导入。 - 方法成员声明为无体
alias,参数类型严格、返回类型精确;嵌套数据形状用伴生type描述,供成员链精确解析。 - 只有被
export标出的类型才对外提供方法签名(如import 'fs' as fs按模块名解析);伴生形状类型仅用于结构解析。 - 异构载荷、回调、变长参数、运行时强制转换的槽位永久保持
<Value>;Value实参传入精确槽位时报错,Dynamic实参跳过校验。 - 运行时导入
.d.alia返回空命名空间,不定义任何变量;class保持原意(定义对象、执行typify、 声明常量),用户代码重定义已有类型名仍报TYPE_ALREADY_DEFINED。 - 刻意绕过静态检查以测试运行时校验时,使用显式
<Dynamic>类型。
6. 泛型:<Value> 与 <Dynamic>
<Value> 表示内容未定的任意类型(代码除外),<Dynamic> 表示完全信任的动态类型。二者用于放宽静态检查,其中 <Dynamic> 须显式声明。
6.1 <Value>
未收窄的 <Value> 在执行需要具体类型的操作时触发 TYPE_VALUE_OPERATION,须先用 typeOf / Ok / as 收窄:
- 算术运算(
+-*/%) - 逻辑运算(
&|)与一元运算(!、一元-) - 条件判断(
if/while/for条件) each目标、索引访问value[...]、属性访问value.prop- 与另一个非字面量值的比较
合法动态桥接:
- 与字面量比较:
value == 42、value == '文本' call value.valueOf:解包底层原始值,返回原类型
alias process <Value data> -> {
if typeOf data == 'Number' {
data * 2
} else {
data
}
}
6.2 <Dynamic>
<Dynamic> 跳过全部类型检查,操作数不触发 TYPE_VALUE_OPERATION。
alias bypass <Dynamic data> -> {
data * 2
}
Rust 后端不支持 <Dynamic>,编译时输出警告并回退到 Value 运行时。
7. 索引推断、赋值检查与类型兼容性
7.1 索引推断
| 目标 | 索引类型 | 推断结果 |
|---|---|---|
数组 array[i] | Number | 元素类型 |
字符串 str[i] | Number | String |
不可变对象 object['key'] | String | 对应属性类型(键为字面量时) |
可变对象 object['key'] | String | Value |
const numbers = [1, 2, 3]
const num = numbers[0] ~ 推断为 Number
const person = *[name: 'Alice', age: 30]
const name = person['name'] ~ 推断为 String
7.2 赋值检查
- 变量赋值
name = val:val类型须可赋值给name的声明类型,否则报TYPE_MISMATCH。 - 成员赋值
obj.prop = val:非变长对象上属性须存在,且val类型可赋值给属性类型。 - 索引赋值
arr[i] = val:数组索引须为Number;对非变长数组赋值报TYPE_NON_VARIADIC_ARRAY_ASSIGNMENT;变长数组校验元素类型兼容性。 - 对象索引赋值
obj['key'] = val:键须 为String,值类型须与属性类型兼容。
7.3 类型兼容性
| 源 → 目标 | 变长数组/对象 | 非变长数组/对象 |
|---|---|---|
| 变长 → | 允许 | 禁止 |
| 非变长 → | 允许 | 允许(属性级别兼容) |
变长源不能赋值给非变长目标;非变长源可赋值给变长目标。类实例沿 extends 链向上兼容父类类型。