# Any 设计

> 

本文档收敛 Dujie 中 `any` 的语言定位、使用边界和显式收窄规则。

## 定位

`any` 是边界类型，不是万能类型，也不是动态对象系统。

它的职责是承接编译期无法完全静态确定的动态值，主要用于语言与宿主环境的边界交互。

当前最重要的使用场景：

- `main(props: map<string, any>) -> widget`
- `element(..., props: map<string, any>, ...)`
- 少量调试、日志和宿主互操作场景

## 基本原则

<steps level="4">

#### `any` 只用于动态边界

#### `T -> any` 允许

#### `any -> T` 不做隐式恢复

#### `any` 不参与普通静态运算

#### `any` 不作为默认推导结果

</steps>

## 允许的方向

### 装箱

普通值可以进入 `any`：

- 基础类型
- `opt<T>`
- `list<T>`
- `map<K, V>`
- `struct`
- `widget`

这里的“进入”只是语言层意义上的动态边界承载，不代表鼓励在普通静态代码里广泛使用 `any`。

### 传递

`any` 可以：

- 赋值给 `any`
- 作为 `any` 参数传递
- 作为函数返回值返回
- 放进 `map<string, any>`

## 禁止的方向

`any` 不允许直接参与这些操作：

- 算术运算
- 比较运算
- 逻辑运算
- 成员访问
- 下标访问
- 直接作为 `if` 条件
- 普通函数调用

也就是说，`any` 不会像动态语言那样自动“猜成某种具体值”后继续运算。

## 显式收窄

第一阶段，`any` 的显式恢复机制统一使用 `is`。

支持的最小语法：

```dj
x is T
x is T(v)
```

语义：

- `x is T`：判断运行时值是否属于 `T`，结果为 `bool`
- `x is T(v)`：判断是否属于 `T`；若成立，把具体值绑定到 `v`

作用域规则：

- `v` 只在判断为真的分支中可见
- `v` 不进入 `else`
- `v` 不泄漏到 `if` 外部

例如：

```dj
let title = props["title"];

if title is string(s) {
    text(s)
} else {
    panic("title must be string")
}
```

## 当前稳定支持的收窄目标

第一阶段稳定支持的目标类型仅限基础类型：

- `int`
- `float`
- `bool`
- `rune`
- `string`

对 `list<T>`、`map<K, V>`、`opt<T>`、`struct`、`widget` 的 `is` 判型，后续如需开放，另行设计。

## 与 `main` 的关系

当用户把 `main` 写成动态入口形式时，`main(props: map<string, any>) -> widget` 是最主要的 `any` 使用边界之一。

规则：

- `props["k"]` 的结果是 `any`
- key 不存在时直接 `panic`
- 取出值后，再通过 `is` 做显式收窄

例如：

```dj
func main(props: map<string, any>) -> widget {
    let title = props["title"];

    if title is string(s) {
        text(s)
    } else {
        panic("title must be string")
    }
}
```

## 与 `element` 的关系

`element.props` 第一阶段保持：

```dj
map<string, any>
```

原因：

- `element` 是宿主边界
- 宿主属性集合天然开放
- 第一阶段不引入联合类型

这里使用 `any`，不代表普通组件参数和普通静态数据模型也应使用 `any`。

## 与类型推导的关系

`any` 不作为“推导失败后的兜底类型”。

也就是说：

- `let x = none` 不会因为推不出类型就变成 `any`
- `[]`、`{}` 也不会因为缺上下文就落成 `list<any>` 或 `map<any, any>`

只有在以下情况下才进入 `any`：

- 用户显式写出 `any`
- 边界 API 明确要求 `any`

## 与插值字符串的关系

插值字符串允许任意类型。

对 `any` 而言，插值采用展示语义：

- 如果运行时能拿到更具体的承载值，则展示该值的展示结果
- 如果拿不到更具体信息，则可以回退成类似 `<any>` 的摘要文本

这只是展示行为，不代表 `any` 在普通表达式中获得了隐式恢复能力。

## 与 `map` 键的关系

`any` 不允许作为 `map` 的 key 类型。

原因：

- key 需要稳定的键语义
- `any` 会把这层语义打散

## 当前不提供的能力

第一阶段不提供：

- `typeof(...)`
- `match`
- 基于 `any` 的模式匹配系统
- `any` 的隐式字段访问
- `any` 的隐式函数调用
- `any` 的隐式转换到基础类型

## 当前结论

`any` 的核心定位可以概括为一句话：

`any` 是用于动态边界和互操作的显式擦除类型，只允许装箱、传递和显式收窄，不允许隐式恢复为具体类型。`
