Civet
编写 TypeScript 的现代方式。
- 文档
- 更新日志
- 设计理念
- Civet 在线试玩
- Civet VSCode 扩展
- Discord 服务器
- 插件支持: Vite, esbuild, Astro, Rollup, Webpack, Rspack, ESM/CJS 加载器, Babel, Jest, Gulp, Bun
- 启动模板:Solid 和 Solid Start
快速入门指南
# 安装
npm install -g @danielx/civet
# 在 REPL 中直接运行 Civet 代码
civet
# 在 REPL 中将带类型的 Civet 代码转译为 TypeScript
civet -c
# 将 Civet 源文件编译为 TypeScript
civet < source.civet > output.ts
# 执行 .civet 脚本
civet source.civet ...args...
# 在 node 中执行 .civet 源文件
node --import @danielx/civet/register source.civet
代码示例
ts, {CompilerOptions} from typescript
DefaultCompilerOptions : CompilerOptions :=
allowNonTsExtensions: true
allowJs: true
target: ts.ScriptTarget.Latest
moduleResolution: ts.ModuleResolutionKind.NodeJs
module: ts.ModuleKind.CommonJS
allowSyntheticDefaultImports: true
experimentalDecorators: true
fileCache : Record<string, any> := {}
createCompilerHost := (options: CompilerOptions, moduleSearchLocations : string[]) ->
fileExists := (fileName: string) : boolean ->
fileCache[fileName]?
readFile := (fileName: string) ->
fileCache[fileName]
概述
Civet 本质上是 TypeScript 的一个优雅的超集。
新的和提议中的 ES 特性的实现
请查看文档以了解这些和其他特性的示例。
- 模式匹配(基于 TC39 提案)
switch
可以匹配如[{type: "text", name}, ...rest]
这样的模式
- 管道操作符(基于 F# 管道、Hack 管道 和 TC39 提案)
data |> Object.keys |> console.log
等同于console.log(Object.keys(data))
- 使用单参数箭头函数或
&
简写来指定如何使用左侧值 |> await
、|> yield
和|> return
(在末尾) 用于包装左侧值的操作
- 简短函数块语法,类似于 Ruby symbol to proc、Crystal 和 Elm 记录访问
- 访问:
x.map &.name
或x.map .name
→x.map(a => a.name)
- 嵌套访问 + 切片:
x.map &.profile?.name[0...3]
→x.map(a => a.profile?.name.slice(0, 3))
- 函数调用:
x.map &.callback a, b
→x.map($ => $.callback(a, b))
- 一元运算符:
x.map !!&
→x.map($ => !!$)
- 二元运算符:
x.map &+1
→x.map($ => $+1)
- 访问:
- 对象字面量简写
{foo()}
→{foo: foo()}
,{props.foo}
→{foo: props.foo}
{`${x}${y}`: z}
→{[`${x}${y}`]: z}
data.{x,y}
或data{x,y}
→{x: data.x, y: data.y}
- 基于 LiveScript 的标记简写:
{+debug, -live, !verbose}
→{debug: true, live: false, verbose: false}
- 任意双参数函数的自定义中缀运算符
do
表达式、if
表达式、for
表达式
ES6+ 特性的便利性
- 常量赋值简写:
a := b
→const a = b
,{a, b} := c
→const {a, b} = c
- let 赋值简写:
a .= b
→let a = b
- 上述的类型版本:
a: number .= 5
→let a: number = 5
(注意a: number = 5
是对象字面量{a: (number = 5)}
) @#id
→this.#id
用于私有标识符的简写import
简写:x from ./x
→import x from "./x"
- 动态
import
简写:非顶层的import './x'
(如await import './x'
或函数内部)→import('./x')
- 可选的 import 重命名语法,对应于解构重命名
import {x: y} from "./z"
→import {x as y} from "./z"
。你仍然可以 使用as
以兼容现有的 ES 导入。 export
简写:export x, y
→export {x, y}
- 三重反引号模板字符串移除前导缩进以提高清晰度
- 类构造函数简写
@( ... )
- 类静态块
@ { ... }
<
作为extends
的简写///
块正则表达式 类似 Python re.X
JSX 增强
受 solid-dsl 讨论 和 jsx spec 问题 的启发
- 缩进:无需显式关闭
<tag>
或<>
, 你可以缩进子元素,Civet 将自动为你关闭标签 - 多个相邻元素和/或片段会自动 组合成一个片段。
- 箭头函数子元素不需要用大括号包裹
(假设它们前面没有文本);这是明确的,因为
>
不是有效的 JSX 文本。例如,<For> (item) => ...
(函数体可以缩进)。 #foo
是id="foo"
的简写; 还有#"foo bar"
、#`foo ${bar}`
、#{expr}
.foo
是class="foo"
的简写(但必须在标签名后至少一个空格); 还有.foo.bar
、."foo bar"
、.`foo ${bar}`
、.{expr}
"civet react"
标志使用className
而不是class
+foo
是foo={true}
的简写,-foo
/!foo
是foo={false}
的简写- 任何大括号对象字面量都可以用作属性:
{foo}
→foo={foo}
,{foo: bar}
→foo={bar}
,{...foo}
保持不变;方法和 getter/setter 也可以工作。 - 属性
...foo
是{...foo}
的简写 - 没有空格或适当包装的属性值
(括号表达式、字符串和模板字符串、
正则表达式、数组字面量、大括号对象字面量)
不需要大括号:
foo=bar
→foo={bar}
,count=count()
→count={count()}
,sum=x+1
→sum={x+1}
,list=[1, 2, 3]
→list={[1, 2, 3]}
- 属性可以使用计算属性名:
[expr]={value}
→{...{[expr]: value}}
"civet solid"
标志为 JSX 元素和片段添加正确的类型。 使用"civet solid client"
(默认)用于仅客户端代码,"civet solid server"
用于仅服务器代码(仅 SSR),或"civet solid client server"
用于在客户端和服务器上运行的同构代码(SSR + 水合)。- XML 注释:
<!-- ... -->
→{/* ... */}
TypeScript 增强
- 自动重写导入中的
.[mc]ts
→.[mc]js
(解决方案:https://github.com/microsoft/TypeScript/issues/37582) :=
只读类字段初始化器class A x := 3
class A { readonly x = 3 }
- 提议:类型化解构
- 提议:类型的点表示法
- 提议:模块接口 https://github.com/microsoft/TypeScript/issues/38511
- 待办:类型声明简写
ES6 的变更
- 即使对于多语句函数,也支持隐式返回
(可以通过指定
void
返回类型、添加尾随;
或显式return
,或通过指令"civet -implicitReturns"
来避免) - 不允许单参数箭头函数省略括号。
x => ...
必须改为(x) => ...
原因是在 CoffeeScript 中x -> ...
等同于x(function() ...)
,让->
和=>
的行为差异更大是不好的。不带括号地将匿名函数传递给应用程序也很方便。 for(i of x) ...
默认为 const 声明 →for(const i of x) ...
- 在条件语句和许多其他地方禁用逗号运算符。不允许
if x, y
。但允许for i = 0, l = a.length; i < l; i++, i *= 2
。 case
/when
中的逗号运算符变成多个条件。- 数字不能以点结尾(否则会与 CoffeeScript 的切片
y[0..x]
产生歧义)。这也意味着你不能用1..toString()
来访问数字的属性,应该使用1.toString()
。当指数跟在点后面时,它被视为属性访问,因为指数可能是一个有效的属性1.e10
→1..e10
。解决方法是添加一个尾随零1.0e10
或删除指数前的点1e10
。 - 额外的保留字
and
、or
、loop
、until
、unless
- 实验性装饰器语法是
@@
而不是@
,因为@
是高价值符号,@id
→this.id
,而且@
也用于静态字段/方法等。@@classDecorator class X @@methodDecorator method() {}
- switch 内的
when
自动中断并添加块级作用域。 - switch 内的
else
添加块级作用域。 - 一元运算符和操作数之间没有空格。条件和三元
?
之间必须有空格,例如x ? a : b
,因为x?
是一元存在运算符。 - 标签写作
:label
(除了 Svelte 的特殊情况$:
)
脚本改进
- Shebang 行在输出中保持不变
#!./node_modules/.bin/ts-node console.log "hi"
与 CoffeeScript 的比较
请查看这个详细的 Civet // CoffeeScript 比较
指令
Civet 不仅仅是一种语言;它可以通过指令以多种方式进行配置,以添加或删除语言特性,或在某些环境中改善行为。 请参阅配置文档。
在 Node.js 环境中使用 Civet
现在你已经确信 Civet 适合你当前/下一个项目。以下是如何设置你的环境以立即提高生产力并获得良好体验。
测试
由于 c8 的源映射集成和 Civet 的源映射,代码覆盖率测试"开箱即用"。
c8 + Mocha
package.json
"scripts": {
"test": "c8 mocha",
...
},
"c8": {
"extension": [
".civet"
]
},
"mocha": {
"extension": [
"civet"
],
"loader": [
"@danielx/civet/esm"
],
...
...
如果你不关心代码覆盖率,可以跳过 c8(但它如此简单,为什么不保留呢?)。
你也可以添加 .js
和 .ts
扩展名,如果你想混合使用的话!如果你 require coffeescript/register
或为它添加一个加载器,甚至 .coffee
也可以工作。
执行测试
yarn test
第 4 步:尽情享受!
开发
使用 Civet Language Server 的 alpha 版本
语言服务器提供语法高亮、自动完成、悬停文档、符号大纲、红色波浪线和转到定义功能。
问: 为什么我不能直接使用 VSCode 内置的 TypeScript LSP?
答: VSCode 内置的 TypeScript LSP 无法解析非 .ts/.js
文件,即使使用插件也不行。也许有一天他们会允许使用插件来调整解析器并插入转译步骤,但在那之前,需要一个单独的语言服务器。
问: 有时文件大纲消失了,红色波浪线都在错误的位置,可能还会弹出一个关于某种 LSP 错误的通知。
答: 我很抱歉发生这种情况,但 Civet Language Server 仍处于 alpha 阶段,正在快速改进。请告诉我具体发生了什么,我会尽力在下次做得更好。
这可能发生在你的 Civet 文件中存在语法错误时。你可以使用 CLI 工具检查它是否能编译。
请提交错误报告/功能请求。
构建
我强烈推荐使用 esbuild 来构建/打包你的 Civet 项目。
import esbuild from 'esbuild'
import civetPlugin from '@danielx/civet/esbuild-plugin'
esbuild.build({
...,
plugins: [
civetPlugin
]
}).catch(() => process.exit(1))
它非常快速,效果很好!
代码覆盖率
赞助
如果你愿意,你可以在 Open Collective 上赞助 Civet。