跳到主要内容

类型的声明与转换

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

转换机制​

按以下顺序解析,命中即返回:

  1. 值与目标同类型时直接返回原值。
  2. 调用值的 to<目标类型> 方法,如 '42'.toNumber。
  3. 调用目标类型的 from<源类型> 方法。
  4. 以上皆无则抛 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]NumberString
不可变对象 object['key']String对应属性类型(键为字面量时)
可变对象 object['key']StringValue
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 链向上兼容父类类型。

下一步​