diff --git a/test/component.glass-easel.test.ts b/test/component.glass-easel.test.ts new file mode 100644 index 0000000..59f1d62 --- /dev/null +++ b/test/component.glass-easel.test.ts @@ -0,0 +1,625 @@ +import { expectType } from 'tsd' + +// wechat-miniprogram/glass-easel: glass-easel-miniprogram-adapter: data_update.test.ts +{ + Component() + .data(() => ({ + arr: [1, 2, 3], + obj: { + sub: false, + }, + })) + .lifetime('attached', function () { + this.groupUpdates(() => { + this.spliceArrayDataOnPath(['arr'], 1, 1, [4, 5]) + this.replaceDataOnPath(['obj', 'sub'], true) + }) + this.groupSetData(() => { + this.updateData({ + arr: [123], + }) + }) + this.applyDataUpdates() + }) + .register() +} + +// wechat-miniprogram/glass-easel: glass-easel-miniprogram-adapter: data_update.test.ts +{ + Component() + .data(() => ({ + a: 123, + })) + .init(function ({ setData, lifetime }) { + lifetime('attached', () => { + setData({ a: 789 }, () => { + this.setData({ a: 456 }, () => {}) + }) + }) + }) + .register() +} + +// wechat-miniprogram/glass-easel: glass-easel-miniprogram-adapter: env.test.ts +{ + Component().register() +} + +// wechat-miniprogram/glass-easel: glass-easel-miniprogram-adapter: env.test.ts +{ + const beh = Behavior({ data: { num: 123 } }) + Component().behavior(beh).register() +} + +// wechat-miniprogram/glass-easel: glass-easel-miniprogram-adapter: selector.test.ts +{ + const childDef= Component() + .lifetime('attached', function () { + // eslint-disable-next-line no-use-before-define + const owner = this.selectOwnerComponent(selfDef)! + owner.update() + }) + .data(() => ({ + a: 123, + })) + .register() + + + const selfDef = Component() + .methods({ + update() { + // eslint-disable-next-line + this.selectComponent('#c1').setData({ a: 456 }) + const c2 = this.selectComponent('#c2', childDef)! + c2.setData({ a: 789 }) + }, + }) + .register() +} + +// wechat-miniprogram/glass-easel: glass-easel-miniprogram-adapter: selector.test.ts +{ + const child1Def = Component() + .data(() => ({ + a: 123, + })) + .export(function () { + return { + id: 1, + set: (d: { a: number }) => { + this.setData(d) + }, + } + }) + .register() + + const child2Def = Component() + .definition({ + data: () => ({ + a: 123, + }), + export() { + return { + id: 2, + set: (d: { a: number }) => { + this.setData(d) + }, + } + }, + }) + .register() + + Component() + .lifetime('attached', function () { + const c1any = this.selectComponent('#c1') as { id: number } + const c1 = this.selectComponent('#c1', child1Def)! + const c2any = this.selectComponent('#c2') as { id: number } + const c2 = this.selectComponent('#c2', child2Def)! + + expectType(c1any.id) + expectType(c1.id) + expectType(c2any.id) + expectType(c2.id) + + c1.set({ a: 456 }) + c2.set({ a: 789 }) + }) + .register() +} + +// wechat-miniprogram/glass-easel: glass-easel-miniprogram-adapter: selector.test.ts +{ + const childDef = Component() + .data(() => ({ + a: 123, + })) + .register() + + Component() + .lifetime('attached', function () { + this.selectAllComponents('.c').forEach((item, i) => item.setData({ a: i })) + this.selectAllComponents('.c', childDef).forEach((item, i) => item.setData({ a: i })) + }) + .register() +} + +// wechat-miniprogram/glass-easel: glass-easel-miniprogram-adapter: selector.test.ts +{ + Component().property('p', String).register() + Component() + .lifetime('attached', function () { + this.createSelectorQuery() + .select('.invalid') + .fields({}) + .select('.s') + .boundingClientRect((res) => { + expectType(res.id) + expectType(res.dataset) + expectType(res.left) + expectType(res.top) + expectType(res.right) + expectType(res.bottom) + expectType(res.width) + expectType(res.height) + }) + .select('#bb') + .scrollOffset((res) => { + expectType(res.id) + expectType(res.dataset) + expectType(res.scrollLeft) + expectType(res.scrollTop) + // expectType(res.scrollWidth) + // expectType(res.scrollHeight) + }) + .select('#cc') + .fields( + { + mark: true, + rect: true, + size: true, + scrollOffset: true, + properties: ['p'], + }, + (res) => { + // FIXME: fields typing + expectType(res) + // expectType(res.id) + // expectType(res.dataset) + // expectType(res.mark) + // expectType(res.left) + // expectType(res.top) + // expectType(res.right) + // expectType(res.bottom) + // expectType(res.width) + // expectType(res.height) + // expectType(res.scrollLeft) + // expectType(res.scrollTop) + // expectType(res.scrollWidth) + // expectType(res.scrollHeight) + // expectType(res.p) + }, + ) + .in(this.selectComponent('#cc')) + .select('#cc') + .boundingClientRect((res) => { + expectType(res) + }) + .exec((r) => { + r.shift() + r.pop() + this.createSelectorQuery().exec(() => { + }) + }) + }) + .register() +} + +// wechat-miniprogram/glass-easel: glass-easel-miniprogram-adapter: selector.test.ts +{ + Component() + .lifetime('attached', function () { + this.createSelectorQuery() + .selectViewport() + .fields( + { + id: true, + dataset: true, + mark: true, + rect: true, + size: true, + scrollOffset: true, + properties: ['p'], + }, + (res) => { + // FIXME: fields typing + expectType(res) + // expectType(res.id) + // expectType(res.dataset) + // expectType(res.mark) + // expectType(res.left) + // expectType(res.top) + // expectType(res.right) + // expectType(res.bottom) + // expectType(res.width) + // expectType(res.height) + // expectType(res.scrollLeft) + // expectType(res.scrollTop) + // expectType(res.scrollWidth) + // expectType(res.scrollHeight) + // expectType(res.p) + }, + ) + .exec() + }) + .register() +} + +// wechat-miniprogram/glass-easel: glass-easel-miniprogram-adapter: selector.test.ts +{ + Component() + .lifetime('attached', function () { + this.createSelectorQuery() + .selectViewport() + .context((res) => { + expectType(res.context) + }) + .exec() + }) + .register() +} + +// wechat-miniprogram/glass-easel: glass-easel-miniprogram-adapter: selector.test.ts +{ + Component() + .lifetime('attached', function () { + const o1 = this.createIntersectionObserver().relativeToViewport() + o1.observe('#a', () => { + /* empty */ + }) + const o2 = this.createIntersectionObserver({ + thresholds: [1], + initialRatio: 0, + observeAll: true, + }).relativeTo('#a') + o2.observe('#b', () => { + /* empty */ + }) + o1.disconnect() + o2.disconnect() + }) + .register() +} + +// wechat-miniprogram/glass-easel: glass-easel-miniprogram-adapter: selector.test.ts +{ + Component() + .lifetime('attached', function () { + const o = this.createMediaQueryObserver() + o.observe({ orientation: 'landscape' }, () => { + /* empty */ + }) + o.disconnect() + }) + .register() +} + +// wechat-miniprogram/glass-easel: glass-easel-miniprogram-adapter: space.test.ts +{ + const beh = Behavior({ + data: { + a: 123, + }, + observers: { + a() { + /* empty */ + }, + }, + }) + + const traitBeh = Behavior.trait<{ a: () => void }>() + + Component() + .definition({ + options: { + propertyEarlyInit: true, + }, + behaviors: [beh], + properties: { + b: Number, + }, + data: () => ({ + c: 789, + }), + observers: [ + { + fields: 'b', + observer() { + /* empty */ + }, + }, + ], + created() { + /* empty */ + }, + attached() { + this.setData({ + a: 321, + b: 654, + } as { b: number }) + /* empty */ + }, + detached() { + /* empty */ + }, + moved() { + /* empty */ + }, + ready() { + /* empty */ + }, + lifetimes: { + invalid() { + /* empty */ + }, + }, + pageLifetimes: { + invalid() { + /* empty */ + }, + }, + relations: { + invalid: { + type: 'parent', + target: traitBeh, + }, + }, + externalClasses: [], + }) + .register() +} + +// wechat-miniprogram/glass-easel: glass-easel-miniprogram-adapter: space.test.ts +{ + Component() + .options({ + pureDataPattern: /^_/, + }) + .data(() => ({ + _a: 123, + b: 456, + })) + .register() +} + +// wechat-miniprogram/glass-easel: glass-easel-miniprogram-adapter: space.test.ts +{ + Component() + .options({ + virtualHost: true, + }) + .register() +} + +// wechat-miniprogram/glass-easel: glass-easel-miniprogram-adapter: space.test.ts +{ + Component().definition({ + options: { + dataDeepCopy: 'none', + }, + data: { + a: [1, 2, 3], + }, + }).register() +} + +// wechat-miniprogram/glass-easel: glass-easel-miniprogram-adapter: space.test.ts +{ + const childDef = Component() + .options({ + virtualHost: true, + dataDeepCopy: 'none', + propertyPassingDeepCopy: 'none' + }) + .property('p', Object) + .register() + + Component() + .options({ + dataDeepCopy: 'none', + propertyPassingDeepCopy: 'none', + }) + .data(() => ({ + a: Object.create(Object.prototype, { + f: { enumerable: true, value: 123 }, + __test: { enumerable: false, value: 456 }, + }), + })) + .lifetime('attached', function () { + const child = this.selectComponent('#c', childDef)! + expectType(child.data.p) + }) + .register() +} + + +// wechat-miniprogram/glass-easel: glass-easel-miniprogram-adapter: space.test.ts +{ + const childType = Component() + .methods({ + childFn() { + return 123 + }, + }) + .lifetime('attached', function () { + expectType(this.selectOwnerComponent(parentType)!.parentFn()) + }) + .register() + + const parentType = Component() + .lifetime('attached', function () { + expectType(this.selectComponent('#c', childType)!.childFn()) + }) + .init(({ method }) => { + const parentFn = method(() => 456) + return { parentFn } + }) + .register() +} + +// wechat-miniprogram/glass-easel: glass-easel-miniprogram-adapter: space.test.ts +{ + Component() + .staticData({ + text: 'abc', + }) + .register() +} + +// wechat-miniprogram/glass-easel: glass-easel-miniprogram-adapter: space.test.ts +{ + Component() + .options({ + virtualHost: true, + }) + .property('a', Number) + .data(() => ({ + aa: 10, + })) + .observer('a', function () { + this.setData({ + aa: this.data.a * 10, + }) + }) + .register() +} + +// wechat-miniprogram/glass-easel: glass-easel-miniprogram-adapter: space.test.ts +{ + Component() + .data(() => ({ + b: 1, + bb: 10, + })) + .observer('b', function () { + this.setData({ + bb: this.data.b * 10, + }) + }) + .lifetime('attached', function () { + this.setData({ b: 2 }) + }) + .register() +} + +// wechat-miniprogram/glass-easel: glass-easel-miniprogram-adapter: space.test.ts +{ + Component() + .options({ + virtualHost: true, + }) + .property('a', Number) + .data(() => ({ + b: 0, + })) + .pageLifetime('show', function () { + this.setData({ + b: 456, + }) + }) + .register() +} + +// wechat-miniprogram/glass-easel: glass-easel-miniprogram-adapter: space.test.ts +{ + Component() + .data(() => ({ + a: 0, + })) + .pageLifetime('show', function (a: number) { + this.setData({ a }) + }) + .register() +} + +// wechat-miniprogram/glass-easel: glass-easel-miniprogram-adapter: space.test.ts +{ + const listDef = Component() + .data(() => ({ + listCount: 0, + })) + .relation('item', { + type: 'child', + linked(target) { + expectType(target.asInstanceOf(itemDef)!.data.itemCount) + this.setData({ + listCount: this.getRelationNodes('item').length, + }) + }, + }) + .register() + + const itemDef = Component() + .data(() => ({ + itemCount: 0, + })) + .init(({ relation }) => { + relation({ + type: 'parent', + target: listDef, + }) + }) + .register() +} + +// TypeSignature: Component register() should carry field types for glass-easel-analyzer +{ + const def = Component() + .property('a', Boolean) + .data(() => ({ + b: 1, + })) + .methods({ + c() {}, + }) + .register() + type FieldTypes = (typeof def)['_$fieldTypes'] + expectType(false as boolean) + expectType(false as boolean) + expectType(1 as number) + expectType(() => {}) +} + +// TypeSignature: Behavior register() should carry field types for glass-easel-analyzer +{ + const beh = Behavior() + .property('a', Number) + .data(() => ({ + b: 'hello', + })) + .methods({ + c() { return 1 }, + }) + .register() + + const def = Component() + .behavior(beh) + .register() + + type FieldTypes = (typeof def)['_$fieldTypes'] + expectType(1 as number) + expectType(1 as number) + expectType('hello' as string) + expectType(() => 1) +} + +// Property value type: precise value type inference (like glass-easel) +{ + Component() + .property('n', { type: Number, value: 1 as const }) + .property('o', { type: Object, value: { f1: 123 } }) + .lifetime('attached', function () { + expectType<1>(this.data.n) + expectType(this.data.o.f1) + }) + .register() +} diff --git a/types/wx/index.d.ts b/types/wx/index.d.ts index d6deb26..36f396b 100644 --- a/types/wx/index.d.ts +++ b/types/wx/index.d.ts @@ -26,9 +26,11 @@ SOFTWARE. /// /// /// +/// /// /// /// +/// declare namespace WechatMiniprogram { type IAnyObject = Record @@ -68,12 +70,6 @@ declare namespace WechatMiniprogram { type AnalyserNode = any type WebGLTexture = any type WebGLRenderingContext = any - - // TODO: fill worklet type - type WorkletFunction = (...args: any) => any - type AnimationObject = any - type SharedValue = T - type DerivedValue = T } declare let console: WechatMiniprogram.Console diff --git a/types/wx/lib.wx.api.d.ts b/types/wx/lib.wx.api.d.ts index 3a56189..2161bd7 100644 --- a/types/wx/lib.wx.api.d.ts +++ b/types/wx/lib.wx.api.d.ts @@ -3854,9 +3854,9 @@ ctx.draw() /** 抬头类型 * * 可选值: - * - 0: 单位; - * - 1: 个人; */ - type: 0 | 1 + * - '0': 单位; + * - '1': 个人; */ + type: '0' | '1' } interface ChooseLicensePlateOption { /** 接口调用结束的回调函数(调用成功、失败都会执行) */ @@ -4618,19 +4618,19 @@ ctx.draw() /** 返回上一级,效果同 `wx.navigateBack`,仅可用于 `worklet` 函数内 */ didPop: (...args: any[]) => any /** 动画控制器,影响推入页面的进入和退出过渡效果 */ - primaryAnimation: SharedValue + primaryAnimation: Skyline.SharedValue /** 动画控制器状态 */ - primaryAnimationStatus: SharedValue + primaryAnimationStatus: Skyline.SharedValue /** 动画控制器,影响栈顶页面的推出过渡效果 */ - secondaryAnimation: SharedValue + secondaryAnimation: Skyline.SharedValue /** 动画控制器状态 */ - secondaryAnimationStatus: SharedValue + secondaryAnimationStatus: Skyline.SharedValue /** 手势开始控制路由,与共享元素动画有关 */ startUserGesture: (...args: any[]) => any /** 手势不再控制路由,与 `startUserGesture` 成对调用 */ stopUserGesture: (...args: any[]) => any /** 当前路由进度由手势控制 */ - userGestureInProgress: SharedValue + userGestureInProgress: Skyline.SharedValue } /** 弹幕内容 */ interface Danmu { @@ -10885,7 +10885,7 @@ NFCAdapter.offDiscovered(listener) // 需传入与监听时同一个的函数对 success?: RequestSubscribeMessageSuccessCallback } interface RequestSubscribeMessageSuccessCallbackResult { - /** [TEMPLATE_ID]是动态的键,即模板id,值包括'accept'、'reject'、'ban'、'filter'。'accept'表示用户同意订阅该条id对应的模板消息,'reject'表示用户拒绝订阅该条id对应的模板消息,'ban'表示已被后台封禁,'filter'表示该模板因为模板标题同名被后台过滤。例如 { errMsg: "requestSubscribeMessage:ok", zun-LzcQyW-edafCVvzPkK4de2Rllr1fFpw2A_x0oXE: "accept"} 表示用户同意订阅zun-LzcQyW-edafCVvzPkK4de2Rllr1fFpw2A_x0oXE这条消息 */ + /** [TEMPLATE_ID]是动态的键,即模板id,值包括'accept'、'reject'、'ban'、'filter'、'acceptWithAlert'、'acceptWithAudio'。'accept'表示用户同意订阅该条id对应的模板消息,'reject'表示用户拒绝订阅该条id对应的模板消息,'ban'表示已被后台封禁,'filter'表示该模板因为模板标题同名被后台过滤,'acceptWithAlert' 表示用户接收消息并打开提醒, 'acceptWithAudio' 表示用户接收订阅消息并开启了语音提醒, 'acceptWithForcePush' 表示用户接收订阅消息并开启了推送提醒。例如 { errMsg: "requestSubscribeMessage:ok", zun-LzcQyW-edafCVvzPkK4de2Rllr1fFpw2A_x0oXE: "accept"} 表示用户同意订阅zun-LzcQyW-edafCVvzPkK4de2Rllr1fFpw2A_x0oXE这条消息 */ [TEMPLATE_ID: string]: string /** 接口调用成功时errMsg值为'requestSubscribeMessage:ok' */ errMsg: string @@ -21745,7 +21745,7 @@ Page({ options: DecayOption, /** 动画完成回调。动画被取消时,返回 fasle,正常完成时返回 true。 */ callback: (...args: any[]) => any - ): AnimationObject + ): Skyline.AnimationObject /** [AnimationObject worklet.delay(number delayMS, AnimationObject delayedAnimation)](https://developers.weixin.qq.com/miniprogram/dev/api/ui/worklet/combine-animation/worklet.delay.html) * * 在插件中使用:不支持 @@ -21755,8 +21755,8 @@ Page({ /** 动画开始前等待的时间,单位:毫秒。 */ delayMS: number, /** 动画对象。 */ - delayedAnimation: AnimationObject - ): AnimationObject + delayedAnimation: Skyline.AnimationObject + ): Skyline.AnimationObject /** [AnimationObject worklet.repeat(AnimationObject animation, number numberOfReps, boolean reverse, function callback)](https://developers.weixin.qq.com/miniprogram/dev/api/ui/worklet/combine-animation/worklet.repeat.html) * * 在插件中使用:不支持 @@ -21764,14 +21764,14 @@ Page({ * 重复执行动画。 */ repeat( /** 动画对象 */ - animation: AnimationObject, + animation: Skyline.AnimationObject, /** 重复次数。为负值时一直循环,直到被取消动画。 */ numberOfReps?: number, /** 反向运行动画,每周期结束动画由尾到头运行。该字段仅对 timing 和 spring 返回的动画对象生效。 */ reverse?: boolean, /** 动画完成回调。动画被取消时,返回 fasle,正常完成时返回 true。 */ callback?: (...args: any[]) => any - ): AnimationObject + ): Skyline.AnimationObject /** [AnimationObject worklet.sequence(AnimationObject animationN)](https://developers.weixin.qq.com/miniprogram/dev/api/ui/worklet/combine-animation/worklet.sequence.html) * * 在插件中使用:不支持 @@ -21779,8 +21779,8 @@ Page({ * 组合动画序列,依次执行传入的动画。 */ sequence( /** 动画对象 */ - animationN: AnimationObject - ): AnimationObject + animationN: Skyline.AnimationObject + ): Skyline.AnimationObject /** [AnimationObject worklet.spring(number|string toValue, Object options, function callback)](https://developers.weixin.qq.com/miniprogram/dev/api/ui/worklet/animation/worklet.spring.html) * * 在插件中使用:不支持 @@ -21793,7 +21793,7 @@ Page({ options: SpringOption, /** 动画完成回调。动画被取消时,返回 fasle,正常完成时返回 true。 */ callback: (...args: any[]) => any - ): AnimationObject + ): Skyline.AnimationObject /** [AnimationObject worklet.timing(number toValue, Object options, function callback)](https://developers.weixin.qq.com/miniprogram/dev/api/ui/worklet/animation/worklet.timing.html) * * 在插件中使用:不支持 @@ -21806,7 +21806,7 @@ Page({ options: TimingOption, /** 动画完成回调。动画被取消时,返回 fasle,正常完成时返回 true。 */ callback: (...args: any[]) => any - ): AnimationObject + ): Skyline.AnimationObject /** [DerivedValue worklet.derived(WorkletFunction updaterWorklet)](https://developers.weixin.qq.com/miniprogram/dev/api/ui/worklet/base/worklet.derived.html) * * 在插件中使用:不支持 @@ -21814,8 +21814,8 @@ Page({ * 衍生值 `DerivedValue`,可基于已有的 `SharedValue` 生成其它共享变量。 */ derived( /** worklet 函数类型,该函数被立即执行,返回值作为 DerivedValue 的初始值。当函数内捕获的 SharedValue 类型值发生变化时,updaterWorklet 被驱动执行,返回值用于更新 DerivedValue。可类比 computed 计算属性进行理解。 */ - updaterWorklet: WorkletFunction - ): DerivedValue + updaterWorklet: Skyline.WorkletFunction + ): Skyline.DerivedValue /** [SharedValue worklet.shared(any initialValue)](https://developers.weixin.qq.com/miniprogram/dev/api/ui/worklet/base/worklet.shared.html) * * 在插件中使用:不支持 @@ -21824,7 +21824,7 @@ Page({ shared( /** 初始值,可通过 `.value` 属性进行读取和修改。类型可以是 `number | string | bool | null | undefined | Object | Array | Function`。 */ initialValue: any - ): SharedValue + ): Skyline.SharedValue /** [function worklet.runOnJS(function fn)](https://developers.weixin.qq.com/miniprogram/dev/api/ui/worklet/tool-function/worklet.runOnJS.html) * * 在插件中使用:不支持 @@ -21851,7 +21851,7 @@ Page({ * 取消由 `SharedValue` 驱动的动画。 */ cancelAnimation( /** 共享变量。 */ - SharedValue: SharedValue + SharedValue: Skyline.SharedValue ): void /** [worklet.scrollViewContext.scrollTo(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/ui/worklet/base/worklet.scrollViewContext.scrollTo.html) * diff --git a/types/wx/lib.wx.behavior.d.ts b/types/wx/lib.wx.behavior.d.ts index 892f3ef..3552724 100644 --- a/types/wx/lib.wx.behavior.d.ts +++ b/types/wx/lib.wx.behavior.d.ts @@ -60,6 +60,8 @@ declare namespace WechatMiniprogram.Behavior { >( options: Options ): Identifier + (): GlassEasel.Behavior.Builder + trait: GlassEasel.TraitBehavior.Constructor } type DataOption = Component.DataOption diff --git a/types/wx/lib.wx.component.d.ts b/types/wx/lib.wx.component.d.ts index 0bf2b59..f9d03a2 100644 --- a/types/wx/lib.wx.component.d.ts +++ b/types/wx/lib.wx.component.d.ts @@ -101,6 +101,7 @@ declare namespace WechatMiniprogram.Component { TIsPage > ): Identifier + (): GlassEasel.Component.Builder } type DataOption = Record type PropertyOption = Record diff --git a/types/wx/lib.wx.glass-easel.behavior.d.ts b/types/wx/lib.wx.glass-easel.behavior.d.ts new file mode 100644 index 0000000..a1078d4 --- /dev/null +++ b/types/wx/lib.wx.glass-easel.behavior.d.ts @@ -0,0 +1,490 @@ +// this rule seems buggy, unable to distinguish WechatMiniprogram.GlassEasel.Component and WechatMiniprogram.Component +/* eslint-disable @typescript-eslint/no-unnecessary-qualifier */ + +declare namespace WechatMiniprogram.GlassEasel.Behavior { + type DefinitionFilter = ( + target: Component.TrivialDefinition, + childFilters: Array< + ((target: Component.TrivialDefinition) => void) | null + > + ) => void + + type ResolveBehaviorBuilder< + B, + TChainingFilter extends TypeUtils.ChainingFilterType + > = + TypeUtils.IsNever extends false + ? TChainingFilter extends TypeUtils.ChainingFilterType + ? Omit & TChainingFilter['add'] + : B + : B + + type Definition< + TData extends TypeUtils.DataList, + TProperty extends TypeUtils.PropertyList, + TMethod extends TypeUtils.MethodList, + TComponentExport + > = { + behaviors?: Array + properties?: TProperty + data?: TData | (() => TData) + observers?: + | Array<{ + fields?: string + observer: TypeUtils.ComponentMethod + }> + | { [fields: string]: TypeUtils.ComponentMethod } + methods?: TMethod + created?: TypeUtils.ComponentMethod + attached?: TypeUtils.ComponentMethod + ready?: TypeUtils.ComponentMethod + moved?: TypeUtils.ComponentMethod + detached?: TypeUtils.ComponentMethod + lifetimes?: { [name: string]: TypeUtils.ComponentMethod } + pageLifetimes?: { [name: string]: TypeUtils.ComponentMethod } + relations?: TypeUtils.RelationParamsWithKey + externalClasses?: string[] + definitionFilter?: DefinitionFilter + export?: (source: Component.TrivialInstance | null) => TComponentExport + } + + interface BaseBuilder< + TPrevData extends TypeUtils.DataList = TypeUtils.Empty, + TData extends TypeUtils.DataList = TypeUtils.Empty, + TProperty extends TypeUtils.PropertyList = TypeUtils.Empty, + TMethod extends TypeUtils.MethodList = TypeUtils.Empty, + TChainingFilter extends TypeUtils.ChainingFilterType = never, + TPendingChainingFilter extends TypeUtils.ChainingFilterType = never, + TComponentExport = never, + TExtraThisFields extends TypeUtils.DataList = TypeUtils.Empty + > { + /** Implement a trait behavior */ + implement< + TIn extends { + [key: string]: any + } + >( + traitBehavior: TraitBehavior.Instance, + impl: TIn + ): ResolveBehaviorBuilder + /** Add external classes */ + externalClasses(list: string[]): this + /** + * Add a data observer + */ + observer< + P extends TypeUtils.ObserverDataPathStrings< + TypeUtils.DataWithPropertyValues + >, + V = TypeUtils.GetFromObserverPathString< + TypeUtils.DataWithPropertyValues, + P + > + >( + paths: P, + func: ( + this: Component.Instance, + newValue: V + ) => void, + once?: boolean + ): ResolveBehaviorBuilder + observer< + P extends Array< + TypeUtils.ObserverDataPathStrings< + TypeUtils.DataWithPropertyValues + > + >, + V = { + [K in keyof P]: TypeUtils.GetFromObserverPathString< + TypeUtils.DataWithPropertyValues, + P[K] + > + } + >( + paths: readonly [...P], + func: ( + this: Component.Instance, + ...newValues: V extends any[] ? V : never + ) => void, + once?: boolean + ): ResolveBehaviorBuilder + /** + * Add a lifetime callback + */ + lifetime( + name: L, + func: ( + this: Component.Instance, + ...args: Parameters + ) => ReturnType, + once?: boolean + ): ResolveBehaviorBuilder + /** + * Add a page-lifetime callback + */ + pageLifetime( + name: string, + func: ( + this: Component.Instance, + ...args: any[] + ) => any, + once?: boolean + ): ResolveBehaviorBuilder + /** + * Add a relation + */ + relation( + name: string, + rel: TypeUtils.RelationParams & + ThisType< + Component.Instance< + TData, + TProperty, + TMethod, + TComponentExport, + TExtraThisFields + > + > + ): ResolveBehaviorBuilder + } + + /** 用于辅助识别 behavior 字段类型的虚拟字段(供 glass-easel-analyzer 等外部模块使用) */ + type TypeSignature< + TData extends TypeUtils.DataList, + TProperty extends TypeUtils.PropertyList, + TMethod extends TypeUtils.MethodList, + > = { + readonly _$behaviorFieldTypes?: { + propertyValues: TypeUtils.PropertyValues + dataWithProperties: TypeUtils.DataWithPropertyValues + methods: TMethod + } + } + + type Instance< + TData extends TypeUtils.DataList, + TProperty extends TypeUtils.PropertyList, + TMethod extends TypeUtils.MethodList, + TChainingFilter extends TypeUtils.ChainingFilterType, + TComponentExport, + TExtraThisFields extends TypeUtils.DataList = TypeUtils.Empty + > = TypeSignature + + type TrivialInstance = Instance< + /* TData */ IAnyObject, + /* TProperty */ IAnyObject, + /* TMethod */ IAnyObject, + /* TChainingFilter */ any, + /* TComponentExport */ any, + /* TExtraThisFields */ IAnyObject + > + + interface Builder< + TPrevData extends TypeUtils.DataList = TypeUtils.Empty, + TData extends TypeUtils.DataList = TypeUtils.Empty, + TProperty extends TypeUtils.PropertyList = TypeUtils.Empty, + TMethod extends TypeUtils.MethodList = TypeUtils.Empty, + TChainingFilter extends TypeUtils.ChainingFilterType = never, + TPendingChainingFilter extends TypeUtils.ChainingFilterType = never, + TComponentExport = never, + TExtraThisFields extends TypeUtils.DataList = TypeUtils.Empty + > extends BaseBuilder< + TPrevData, + TData, + TProperty, + TMethod, + TChainingFilter, + TPendingChainingFilter, + TComponentExport, + TExtraThisFields + > { + /** Define a chaining filter */ + chainingFilter< + TAddedFields extends { + [key: string]: any + }, + TRemovedFields extends string = never + >( + func: TypeUtils.ChainingFilterFunc + ): ResolveBehaviorBuilder< + Builder< + TPrevData, + TData, + TProperty, + TMethod, + TChainingFilter, + { + add: TAddedFields + remove: TRemovedFields + }, + TComponentExport, + TExtraThisFields + >, + TChainingFilter + > + /** Use another behavior */ + behavior< + UData extends TypeUtils.DataList, + UProperty extends TypeUtils.PropertyList, + UMethod extends TypeUtils.MethodList, + UChainingFilter extends TypeUtils.ChainingFilterType, + UComponentExport, + UExtraThisFields extends TypeUtils.DataList = TypeUtils.Empty + >( + behavior: + | Instance< + UData, + UProperty, + UMethod, + UChainingFilter, + UComponentExport, + UExtraThisFields + > + | WechatMiniprogram.Behavior.Identifier + ): ResolveBehaviorBuilder< + Builder< + TPrevData, + TData & UData, + TProperty & UProperty, + TMethod & UMethod, + UChainingFilter, + TPendingChainingFilter, + UComponentExport, + TExtraThisFields & UExtraThisFields + >, + UChainingFilter + > + /** Set the export value when the component is being selected */ + export( + f: ( + this: Component.TrivialInstance, + source: Component.TrivialInstance | null + ) => TNewComponentExport + ): ResolveBehaviorBuilder< + Builder< + TPrevData, + TData, + TProperty, + TMethod, + TChainingFilter, + TPendingChainingFilter, + TNewComponentExport, + TExtraThisFields + >, + TChainingFilter + > + /** + * Add some template data fields + * + * It does not support raw data, but a `gen` function which returns the new data fields. + * The `gen` function executes once during component instance creation. + */ + data( + gen: () => TypeUtils.NewFieldList< + TypeUtils.DataWithPropertyValues, + T + > + ): ResolveBehaviorBuilder< + Builder< + T, + TData & T, + TProperty, + TMethod, + TChainingFilter, + TPendingChainingFilter, + TComponentExport, + TExtraThisFields + >, + TChainingFilter + > + /** + * Add some template data fields + * + * The data should be JSON-compatible, and will be cloned during component creation. + */ + staticData( + data: TypeUtils.NewFieldList< + TypeUtils.DataWithPropertyValues, + T + > + ): ResolveBehaviorBuilder< + Builder< + T, + TData & T, + TProperty, + TMethod, + TChainingFilter, + TPendingChainingFilter, + TComponentExport, + TExtraThisFields + >, + TChainingFilter + > + /** + * Add a single property + * + * The property name should be different from other properties. + */ + property< + N extends string, + T extends TypeUtils.PropertyType, + V extends TypeUtils.PropertyTypeToValueType + >( + name: N, + def: N extends keyof (TData & TProperty) + ? never + : TypeUtils.PropertyListItem + ): ResolveBehaviorBuilder< + Builder< + TPrevData, + TData, + TProperty & + Record< + N, + unknown extends V ? T : TypeUtils.PropertyOption + >, + TMethod, + TChainingFilter, + TPendingChainingFilter, + TComponentExport, + TExtraThisFields + >, + TChainingFilter + > + /** + * Add some public methods + * + * The public method can be used as an event handler, and can be visited in component instance. + */ + methods( + funcs: T & + ThisType< + Component.Instance + > + ): ResolveBehaviorBuilder< + Builder< + TPrevData, + TData, + TProperty, + TMethod & T, + TChainingFilter, + TPendingChainingFilter, + TComponentExport, + TExtraThisFields + >, + TChainingFilter + > + /** + * Execute a function while component instance creation + * + * A `BuilderContext` is provided to tweak the component creation progress. + * The return value is used as the "export" value of the behavior. + */ + init< + TExport extends Record< + string, + TypeUtils.TaggedMethod<(...args: any[]) => any> + > | void + >( + func: ( + this: Component.Instance< + TData, + TProperty, + TMethod, + TComponentExport, + TExtraThisFields + >, + builderContext: TypeUtils.BuilderContext< + TPrevData, + TProperty, + Component.Instance< + TData, + TProperty, + TMethod, + TComponentExport, + TExtraThisFields + > + > + ) => TExport + ): ResolveBehaviorBuilder< + Builder< + TPrevData, + TData, + TProperty, + TMethod & + (TExport extends void + ? TypeUtils.Empty + : { + [K in keyof TExport]: TypeUtils.UnTaggedMethod + }), + TChainingFilter, + TPendingChainingFilter, + TComponentExport, + TExtraThisFields + >, + TChainingFilter + > + /** Apply a classic definition object */ + definition< + TNewData extends TypeUtils.DataList = TypeUtils.Empty, + TNewProperty extends TypeUtils.PropertyList = TypeUtils.Empty, + TNewMethod extends TypeUtils.MethodList = TypeUtils.Empty, + TNewComponentExport = never + >( + def: Definition< + TNewData, + TNewProperty, + TNewMethod, + TNewComponentExport + > & + ThisType< + Component.Instance< + TData & TNewData, + TProperty & TNewProperty, + TMethod & TNewMethod, + TNewComponentExport, + TExtraThisFields + > + > + ): ResolveBehaviorBuilder< + Builder< + TPrevData, + TData & TNewData, + TProperty & TNewProperty, + TMethod & TNewMethod, + TChainingFilter, + TPendingChainingFilter, + TNewComponentExport, + TExtraThisFields + >, + TChainingFilter + > + /** + * Finish the behavior definition process + */ + register(): Instance< + TData, + TProperty, + TMethod, + TPendingChainingFilter, + TComponentExport, + TExtraThisFields + > + /** + * Add extra this fields type + */ + extraThisFieldsType(): ResolveBehaviorBuilder< + Builder< + TPrevData, + TData, + TProperty, + TMethod, + TChainingFilter, + TPendingChainingFilter, + TComponentExport, + TExtraThisFields & T + >, + TChainingFilter + > + } +} diff --git a/types/wx/lib.wx.glass-easel.component.d.ts b/types/wx/lib.wx.glass-easel.component.d.ts new file mode 100644 index 0000000..23fc8e1 --- /dev/null +++ b/types/wx/lib.wx.glass-easel.component.d.ts @@ -0,0 +1,716 @@ +// this rule seems buggy, unable to distinguish WechatMiniprogram.GlassEasel.Component and WechatMiniprogram.Component +/* eslint-disable @typescript-eslint/no-unnecessary-qualifier */ + +declare namespace WechatMiniprogram.GlassEasel.Component { + type ExportType< + UData extends TypeUtils.DataList, + UProperty extends TypeUtils.PropertyList, + UMethod extends TypeUtils.MethodList, + UComponentExport, + UExtraThisFields extends TypeUtils.DataList + > = [UComponentExport] extends [never] + ? Instance + : UComponentExport + + type DefinitionOptions = { + multipleSlots?: boolean + dynamicSlots?: boolean + pureDataPattern?: RegExp + virtualHost?: boolean + dataDeepCopy?: 'none' | 'simple' | 'simple-recursion' + propertyPassingDeepCopy?: 'none' | 'simple' | 'simple-recursion' + propertyEarlyInit?: boolean + /** @deprecated prefer static config */ + addGlobalClass?: boolean + /** @deprecated prefer static config */ + styleIsolation?: + | 'isolated' + | 'apply-shared' + | 'shared' + | 'page-isolated' + | 'page-apply-shared' + | 'page-shared' + } + + type Definition< + TData extends TypeUtils.DataList, + TProperty extends TypeUtils.PropertyList, + TMethod extends TypeUtils.MethodList, + TComponentExport + > = { + options?: DefinitionOptions + } & Behavior.Definition + + type TrivialDefinition = Definition< + IAnyObject, + IAnyObject, + IAnyObject, + IAnyObject + > + + type PageDefinition< + TData extends TypeUtils.DataList, + TExtraFields extends { [k: PropertyKey]: any } + > = TExtraFields & { + options?: DefinitionOptions + behaviors?: Behavior.TrivialInstance[] + data?: TData + } + + type Instance< + TData extends TypeUtils.DataList, + TProperty extends TypeUtils.PropertyList, + TMethod extends TypeUtils.MethodList, + TComponentExport, + TExtraThisFields extends TypeUtils.DataList = TypeUtils.Empty + > = TMethod & TExtraThisFields & { + /** The component path in the code space */ + is: string + /** The `id` field in the template */ + id: string + getPageId(): string + router: Router + pageRouter: Router + renderer: 'webview' | 'skyline' + /** The `data-*` field in the template */ + dataset: { + [key: string]: any + } + /** The component data and property values */ + data: TypeUtils.Merge< + TypeUtils.DataWithPropertyValues + > + + /** The component data and property values (same as `data` ) */ + properties: TypeUtils.Merge< + TypeUtils.DataWithPropertyValues + > + + /** + * Group several data update calls + * + * This method is designed for hinting the backend that some updates should be handled together. + * However, this is done automatically now, + * so this method is just for backward compatibilities. + */ + groupSetData(callback: () => void): void + /** + * Do a classic data update + * + * The `callback` is called after the update applies in the backend. + * In most cases, you SHOULD NOT wait the backend update (that might be very slow), + * and most calls, including read calls such as `selectComponent` , simply works immediately. + * However, when called inside observers, + * the data update will not be applied to templates immediately + * (it is recommanded to use `updateData` instead in observers). + */ + setData( + newData: Partial< + TypeUtils.SetDataSetter< + TypeUtils.DataWithPropertyValues + > + >, + callback?: () => void + ): void + setData(data: Record, callback?: () => void): void + /** + * Schedule a classic data updates + * + * The data update will not be applied until next `setData` or `applyDataUpdates` call. + * When called inside observers, the data update will be applied when observer ends. + * All data observers will not be triggered immediately before applied. + * Reads of the data will get the unchanged value before applied. + */ + updateData( + newData: Partial< + TypeUtils.SetDataSetter< + TypeUtils.DataWithPropertyValues + > + > + ): void + updateData(newData: Record): void + /** + * Schedule a data update on a single specified path + * + * The data update will not be applied until next `setData` or `applyDataUpdates` call. + * All data observers will not be triggered immediately before applied. + * Reads of the data will get the unchanged value before applied. + */ + replaceDataOnPath>( + path: readonly [...T], + data: TypeUtils.GetFromDataPath< + TypeUtils.DataWithPropertyValues, + T + > + ): void + replaceDataOnPath(path: any, data: any): void + /** + * Schedule an array update + * + * The behavior is like `Array.prototype.slice` . + * Break the array before the `index`-th item, delete `del` items, and insert some items here. + * If `index` is undefined, negative, or larger than the length of the array, + * no items will be deleted and new items will be appended to the end of the array. + * The data update will not be applied until next `setData` or `applyDataUpdates` call. + * All data observers will not be triggered immediately before applied. + * Reads of the data will get the unchanged value before applied. + */ + spliceArrayDataOnPath>( + path: readonly [...T], + index: TypeUtils.GetFromDataPath< + TypeUtils.DataWithPropertyValues, + T + > extends any[] + ? number | undefined + : never, + del: TypeUtils.GetFromDataPath< + TypeUtils.DataWithPropertyValues, + T + > extends any[] + ? number | undefined + : never, + inserts: TypeUtils.GetFromDataPath< + TypeUtils.DataWithPropertyValues, + T + > extends Array + ? I[] + : never + ): void + spliceArrayDataOnPath( + path: Array, + index: number | undefined, + del: number | undefined, + inserts: unknown[] + ): void + /** + * Apply all scheduled updates immediately + * + * Inside observers, it is generally not . + */ + applyDataUpdates(): void + /** + * Pending all data updates in the callback, and apply updates after callback returns + * + * This function helps grouping several `replaceDataOnPath` or `spliceArrayDataOnPath` calls, + * and then apply them at the end of the callback. + * `setData` and `applyDataUpdates` calls inside the callback still apply updates immediately. + */ + groupUpdates(callback: () => T): T | undefined + /** + * Check whether the `other` behavior is a dependent behavior or a implemented trait behavior + */ + hasBehavior( + behavior: + | Behavior.TrivialInstance + | TraitBehavior.Instance + ): boolean + /** + * Get the trait behavior implementation of the component + * + * Returns `undefined` if the specified trait behavior is not implemented. + */ + traitBehavior>( + traitBehavior: TraitBehavior.Instance + ): TOut | undefined + /** Trigger an event */ + triggerEvent( + name: string, + detail: any, + options: { + bubbles?: boolean + composed?: boolean + capturePhase?: boolean + } + ): void + /** Create a selector query for searching element inside the component */ + createSelectorQuery(): SelectorQuery + applyAnimatedStyle( + selector: string, + updater: Skyline.WorkletFunction, + userConfig?: Skyline.AnimatedStyleConfig, + callback?: (styleId: number) => void + ): void + clearAnimatedStyle( + selector: string, + styleIds: number[], + callback?: () => void + ): void + /** Create an intersection observer */ + createIntersectionObserver(options?: { + thresholds?: number[] + initialRatio?: number + observeAll?: boolean + }): IntersectionObserver + /** Create an media query observer */ + createMediaQueryObserver(): MediaQueryObserver + getOpenerEventChannel(): EventChannel + /** + * Query an element inside the component + * + * If `componentType` is provided, this method will check the selected component type. + * If the component type does not match, `null` is returned. + */ + selectComponent(selector: string): any + selectComponent< + UData extends TypeUtils.DataList, + UProperty extends TypeUtils.PropertyList, + UMethod extends TypeUtils.MethodList, + UComponentExport, + UExtraThisFields extends TypeUtils.DataList = TypeUtils.Empty + >( + selector: string, + componentType: ComponentType< + UData, + UProperty, + UMethod, + UComponentExport, + UExtraThisFields + > + ): ExportType | null + /** + * Query all elements inside the component + * + * If `componentType` is provided, this method will check the selected component type. + * If a component type does not match, it is not returned. + */ + selectAllComponents(selector: string): TrivialInstance[] + selectAllComponents< + UData extends TypeUtils.DataList, + UProperty extends TypeUtils.PropertyList, + UMethod extends TypeUtils.MethodList, + UComponentExport, + UExtraThisFields extends TypeUtils.DataList = TypeUtils.Empty + >( + selector: string, + componentType: ComponentType< + UData, + UProperty, + UMethod, + UComponentExport, + UExtraThisFields + > + ): Array> + /** + * Get the owner component + * + * If `componentType` is provided, this method will check the selected component type. + * If a component type does not match, `null` is returned. + */ + selectOwnerComponent(): any + selectOwnerComponent< + UData extends TypeUtils.DataList, + UProperty extends TypeUtils.PropertyList, + UMethod extends TypeUtils.MethodList, + UComponentExport, + UExtraThisFields extends TypeUtils.DataList = TypeUtils.Empty + >( + componentType: ComponentType< + UData, + UProperty, + UMethod, + UComponentExport, + UExtraThisFields + > + ): ExportType | null + getRelationNodes(relationKey: string): TrivialInstance[] + applyAnimation( + selector: string, + options: WechatMiniprogram.Component.KeyFrame, + _animeJS: boolean, + callback?: () => void + ): void + clearAnimation( + selector: string, + options: + | { + [K in keyof WechatMiniprogram.Component.KeyFrame]?: boolean + } + | undefined + | null, + callback?: () => void + ): void + animate( + selector: string, + keyframes: WechatMiniprogram.Component.KeyFrame[], + duration: number, + callback?: () => void + ): void + animate( + selector: string, + keyframes: WechatMiniprogram.Component.KeyFrame[], + duration: number, + scrollTimeline: WechatMiniprogram.Component.ScrollTimelineOption + ): void + getTabBar(cb?: (tabBar: TrivialInstance) => void): TrivialInstance + getAppBar(): TrivialInstance + /** The `id` field in the template */ + get exitState(): any + set exitState(value: any) + setInitialRenderingCache(): void + setUpdatePerformanceListener( + config: WechatMiniprogram.Component.SetUpdatePerformanceListenerOption, + listener: WechatMiniprogram.Component.UpdatePerformanceListener + ): void + getPassiveEvent(cb: any): void + setPassiveEvent(config: any): void + /** + * Cast the component into the specified type + * + * Returns `null` if the component node is not the instance of the specified component. + */ + asInstanceOf< + UData extends TypeUtils.DataList, + UProperty extends TypeUtils.PropertyList, + UMethod extends TypeUtils.MethodList, + UComponentExport, + UExtraThisFields extends TypeUtils.DataList = TypeUtils.Empty + >( + componentType: ComponentType< + UData, + UProperty, + UMethod, + UComponentExport, + UExtraThisFields + > + ): Instance | null + } + + interface TrivialInstance + extends Instance< + IAnyObject, + IAnyObject, + IAnyObject, + IAnyObject, + IAnyObject + > {} + + /** 用于辅助识别组件类型的虚拟字段(供 glass-easel-analyzer 等外部模块使用) */ + class TypeSignature< + TData extends TypeUtils.DataList, + TProperty extends TypeUtils.PropertyList, + TMethod extends TypeUtils.MethodList, + > { + protected readonly _$fieldTypes: { + propertyValues: TypeUtils.PropertyValues + dataWithProperties: TypeUtils.DataWithPropertyValues + methods: TMethod + } + } + + type ComponentType< + TData extends TypeUtils.DataList, + TProperty extends TypeUtils.PropertyList, + TMethod extends TypeUtils.MethodList, + TComponentExport, + TExtraThisFields extends TypeUtils.DataList = TypeUtils.Empty + > = TypeSignature + + type TrivialComponentType = ComponentType< + TypeUtils.DataList, + TypeUtils.PropertyList, + TypeUtils.MethodList, + any, + TypeUtils.DataList + > + + interface Builder< + TPrevData extends TypeUtils.DataList = TypeUtils.Empty, + TData extends TypeUtils.DataList = TypeUtils.Empty, + TProperty extends TypeUtils.PropertyList = TypeUtils.Empty, + TMethod extends TypeUtils.MethodList = TypeUtils.Empty, + TChainingFilter extends TypeUtils.ChainingFilterType = never, + TPendingChainingFilter extends TypeUtils.ChainingFilterType = never, + TComponentExport = never, + TExtraThisFields extends TypeUtils.DataList = TypeUtils.Empty + > extends Behavior.BaseBuilder< + TPrevData, + TData, + TProperty, + TMethod, + TChainingFilter, + TPendingChainingFilter, + TComponentExport, + TExtraThisFields + > { + /** + * Set the component options + * + * If called multiple times, only the latest call is valid. + */ + options( + options: DefinitionOptions + ): Behavior.ResolveBehaviorBuilder + /** Use another behavior */ + behavior< + UData extends TypeUtils.DataList, + UProperty extends TypeUtils.PropertyList, + UMethod extends TypeUtils.MethodList, + UChainingFilter extends TypeUtils.ChainingFilterType, + UComponentExport, + UExtraThisFields extends TypeUtils.DataList = TypeUtils.Empty + >( + behavior: + | Behavior.Instance< + UData, + UProperty, + UMethod, + UChainingFilter, + UComponentExport, + UExtraThisFields + > + | WechatMiniprogram.Behavior.Identifier + ): Behavior.ResolveBehaviorBuilder< + Builder< + TPrevData, + TData & UData, + TProperty & UProperty, + TMethod & UMethod, + UChainingFilter, + TPendingChainingFilter, + UComponentExport, + TExtraThisFields & UExtraThisFields + >, + UChainingFilter + > + /** Set the export value when the component is being selected */ + export( + f: ( + this: TrivialInstance, + source: TrivialInstance | null + ) => TNewComponentExport + ): Behavior.ResolveBehaviorBuilder< + Builder< + TPrevData, + TData, + TProperty, + TMethod, + TChainingFilter, + TPendingChainingFilter, + TNewComponentExport, + TExtraThisFields + >, + TChainingFilter + > + /** + * Add some template data fields + * + * It does not support raw data, but a `gen` function which returns the new data fields. + * The `gen` function executes once during component instance creation. + */ + data( + gen: () => TypeUtils.NewFieldList< + TypeUtils.DataWithPropertyValues, + T + > + ): Behavior.ResolveBehaviorBuilder< + Builder< + T, + TData & T, + TProperty, + TMethod, + TChainingFilter, + TPendingChainingFilter, + TComponentExport, + TExtraThisFields + >, + TChainingFilter + > + /** + * Add some template data fields + * + * The data should be JSON-compatible, and will be cloned during component creation. + */ + staticData( + data: TypeUtils.NewFieldList< + TypeUtils.DataWithPropertyValues, + T + > + ): Behavior.ResolveBehaviorBuilder< + Builder< + T, + TData & T, + TProperty, + TMethod, + TChainingFilter, + TPendingChainingFilter, + TComponentExport, + TExtraThisFields + >, + TChainingFilter + > + /** + * Add a single property + * + * The property name should be different from other properties. + */ + property< + N extends string, + T extends TypeUtils.PropertyType, + V extends TypeUtils.PropertyTypeToValueType + >( + name: N, + def: N extends keyof (TData & TProperty) + ? never + : TypeUtils.PropertyListItem + ): Behavior.ResolveBehaviorBuilder< + Builder< + TPrevData, + TData, + TProperty & + Record< + N, + unknown extends V ? T : TypeUtils.PropertyOption + >, + TMethod, + TChainingFilter, + TPendingChainingFilter, + TComponentExport, + TExtraThisFields + >, + TChainingFilter + > + /** + * Add some public methods + * + * The public method can be used as an event handler, and can be visited in component instance. + */ + methods( + funcs: T & ThisType> + ): Behavior.ResolveBehaviorBuilder< + Builder< + TPrevData, + TData, + TProperty, + TMethod & T, + TChainingFilter, + TPendingChainingFilter, + TComponentExport, + TExtraThisFields + >, + TChainingFilter + > + /** + * Execute a function while component instance creation + * + * A `BuilderContext` is provided to tweak the component creation progress. + * The return value is used as the "export" value of the behavior, + * which can be imported by other behaviors. + */ + init< + TExport extends Record< + string, + TypeUtils.TaggedMethod<(...args: any[]) => any> + > | void + >( + func: ( + this: Instance, + builderContext: TypeUtils.BuilderContext< + TPrevData, + TProperty, + Instance + > + ) => TExport + ): Behavior.ResolveBehaviorBuilder< + Builder< + TPrevData, + TData, + TProperty, + TMethod & + (TExport extends void + ? TypeUtils.Empty + : { + [K in keyof TExport]: TypeUtils.UnTaggedMethod + }), + TChainingFilter, + TPendingChainingFilter, + TComponentExport, + TExtraThisFields + >, + TChainingFilter + > + /** Apply a classic definition object */ + definition< + TNewData extends TypeUtils.DataList = TypeUtils.Empty, + TNewProperty extends TypeUtils.PropertyList = TypeUtils.Empty, + TNewMethod extends TypeUtils.MethodList = TypeUtils.Empty, + TNewComponentExport = never + >( + def: Definition< + TNewData, + TNewProperty, + TNewMethod, + TNewComponentExport + > & + ThisType< + Instance< + TData & TNewData, + TProperty & TNewProperty, + TMethod & TNewMethod, + TNewComponentExport, + TExtraThisFields + > + > + ): Behavior.ResolveBehaviorBuilder< + Builder< + TPrevData, + TData & TNewData, + TProperty & TNewProperty, + TMethod & TNewMethod, + TChainingFilter, + TPendingChainingFilter, + TNewComponentExport, + TExtraThisFields + >, + TChainingFilter + > + pageDefinition< + TNewData extends TypeUtils.DataList, + TNewExtraFields extends { + [k: PropertyKey]: any + } + >( + def: PageDefinition & + ThisType< + Instance< + TData & TNewData, + TProperty, + TMethod & TNewExtraFields, + undefined, + TExtraThisFields + > + > + ): Behavior.ResolveBehaviorBuilder< + Builder< + TPrevData, + TData & TNewData, + TProperty, + TMethod & TNewExtraFields, + TChainingFilter, + TPendingChainingFilter, + TComponentExport, + TExtraThisFields + >, + TChainingFilter + > + /** + * Finish the component definition process + */ + register(): ComponentType + /** + * Add extra this fields type + */ + extraThisFieldsType(): Behavior.ResolveBehaviorBuilder< + Builder< + TPrevData, + TData, + TProperty, + TMethod, + TChainingFilter, + TPendingChainingFilter, + TComponentExport, + TExtraThisFields & T + >, + TChainingFilter + > + } +} diff --git a/types/wx/lib.wx.glass-easel.d.ts b/types/wx/lib.wx.glass-easel.d.ts new file mode 100644 index 0000000..6e64188 --- /dev/null +++ b/types/wx/lib.wx.glass-easel.d.ts @@ -0,0 +1,4 @@ +/// +/// +/// +/// diff --git a/types/wx/lib.wx.glass-easel.trait-behavior.d.ts b/types/wx/lib.wx.glass-easel.trait-behavior.d.ts new file mode 100644 index 0000000..203d1c4 --- /dev/null +++ b/types/wx/lib.wx.glass-easel.trait-behavior.d.ts @@ -0,0 +1,13 @@ +declare namespace WechatMiniprogram.GlassEasel.TraitBehavior { + type Instance< + TIn extends { [key: string]: any }, + TOut extends { [key: string]: any } = TIn + > = {} + + interface Constructor { + >(): Instance + , TOut extends Record>( + trans: (impl: TIn) => TOut + ): Instance + } +} diff --git a/types/wx/lib.wx.glass-easel.utils.d.ts b/types/wx/lib.wx.glass-easel.utils.d.ts new file mode 100644 index 0000000..c6e77f7 --- /dev/null +++ b/types/wx/lib.wx.glass-easel.utils.d.ts @@ -0,0 +1,526 @@ +declare namespace WechatMiniprogram.GlassEasel.TypeUtils { + type Empty = Record + + type IsEmpty = Equal + + type NewField = + Extract extends never ? TValueType : never + + type NewFieldList = + Extract extends never + ? TNewObject + : never + + type Equal = + (() => T extends X ? 1 : 2) extends () => T extends Y ? 1 : 2 + ? true + : false + + /** + * UnionToIntersection<'foo' | 42 | true> = 'foo' & 42 & true + * UnionToIntersection<(() => 'foo') | ((i: 42) => true)> = (() => 'foo') & ((i: 42) => true) + */ + type UnionToIntersection = ( + T extends unknown ? (arg: T) => void : never + ) extends (args: infer Arg) => void + ? Arg + : never + + /** + * Merge<{ foo: string }, { bar: number }> = { foo: string, bar: number } + */ + type Merge = U extends infer T ? { [K in keyof T]: T[K] } : never + + /** + * IsAny = true + * IsAny<{}> = false + */ + type IsAny = + ((S: S) => S extends T ? 1 : 2) extends ( + R: R + ) => R extends any ? 1 : 2 + ? true + : false + + /** + * IsNever = true + * IsNever = false + * IsNever = false + */ + type IsNever = [T] extends [never] ? true : false + + type SetDataStringPath = [ + Prefix + ] extends [never] + ? `${K}` + : K extends number + ? `${Prefix}[${K}]` + : `${Prefix}.${K}` + + type Tuple = []> = 0 extends 1 + ? never + : Res['length'] extends T + ? Res + : Tuple + + type Subtract = + Tuple extends [...Tuple, ...infer Rest] ? Rest['length'] : never + + /** + * SetDataSetter<{ name: string; foo: { bar: number } }> = { + * name: string, + * foo: { bar: number }, + * 'foo.bar': number, + * } + * setDataSetter<{ list: number[], foo: { bar: number }[]}> = { + * list: number[], + * `list[${number}]`: number, + * foo: { bar: number }[], + * `foo[${number}]`: { bar: number }[], + * `foo[${number}].bar`: number, + * } + */ + type SetDataSetter< + T, + Prefix extends string = never, + Count extends number = 4 + > = Count extends 0 + ? Record, T> + : IsAny extends true + ? Record, T> + : UnionToIntersection< + T extends any[] + ? { + [P in keyof T & number]: SetDataSetter< + T[P], + SetDataStringPath, + Subtract + > & + Record, T[P]> + }[keyof T & number] + : T extends Record + ? { + [P in keyof T & (string | number)]: SetDataSetter< + T[P], + SetDataStringPath, + Subtract + > & + Record, T[P]> + }[keyof T & (string | number)] + : never + > + + /** + * DeepReadonly<{ foo: { bar: number } }> = { + * readonly foo: { + * readonly bar: number + * } + * } + */ + type DeepReadonly = Count extends 0 + ? T + : T extends Record + ? T extends (...args: any[]) => any + ? T + : { + readonly [P in keyof T]: DeepReadonly< + T[P], + Subtract + > + } + : T + + type PublicFields = { + [K in keyof T as K extends `_$${any}` ? never : K]: T[K] + } + + /** + * ObjectDataPathStrings<{ name: string; age: number }> = 'name' | 'age' + * ObjectDataPathStrings<{ + * refCount: number; + * person: { name: string; age: number }; + * }> = 'refCount' | 'person' | 'person.name' | 'person.age' + * ObjectDataPathStrings<{ books: [{ name: string; price: number }] }> = + * 'books' | `books[${number}]` | `books[${number}].name` | `books[${number}].price` + */ + type ObjectDataPathStrings< + T, + Prefix extends string = never, + Count extends number = 4 + > = Count extends 0 + ? SetDataStringPath + : IsAny extends true + ? SetDataStringPath + : T extends any[] + ? { + [P in keyof T & number]: + | SetDataStringPath + | ObjectDataPathStrings< + T[P], + SetDataStringPath, + Subtract + > + }[keyof T & number] + : T extends Record + ? { + [P in keyof T & (string | number)]: + | SetDataStringPath + | ObjectDataPathStrings< + T[P], + SetDataStringPath, + Subtract + > + }[keyof T & (string | number)] + : Prefix + + type ObserverDataPathStrings< + T, + S extends string = ObjectDataPathStrings + > = '**' | S | `${S}.**` + + /** + * GetFromDataPathString<{ name: string; age: number }, 'name'> = string + * GetFromDataPathString<{ person: { name: string; age: number } }, 'person.name'> = string + * GetFromDataPathString<{ books: [{ name: string; price: number }] }, 'books[0].name'> = string + */ + type GetFromDataPathString = P extends keyof T + ? T[P] + : P extends '' + ? T + : P extends `[${infer K extends keyof T & number}].${infer R}` + ? GetFromDataPathString + : P extends `[${infer K extends keyof T & number}]${infer R}` + ? GetFromDataPathString + : P extends `${infer K extends keyof T & string}[${infer R}` + ? GetFromDataPathString + : P extends `${infer K extends keyof T & string}.${infer R}` + ? GetFromDataPathString + : never + + type GetFromObserverPathString = P extends '**' + ? GetFromDataPathString + : P extends `${infer K}.**` + ? GetFromDataPathString + : GetFromDataPathString + + /** + * GetFromDataPath<{ foo: { bar: number } }, ['foo', 'bar']> = number + * GetFromDataPath<{ list: { bar: number }[] }, ['list', 0, 'bar']> = number + * GetFromDataPath<{ list: number }, ['nonExists']> = never + */ + type GetFromDataPath< + T, + K extends ReadonlyArray + > = K extends [infer F, ...infer R extends Array] + ? F extends keyof T + ? GetFromDataPath + : never + : T + + const TaggedSymbol: unique symbol + type Tagged = typeof TaggedSymbol + + type IfNeverOrAny = [T] extends [never] + ? Replacement + : 1 extends T & 0 + ? Replacement + : T + + type GetTags = B extends { + readonly [Tag in Tagged]: infer Tags extends symbol[] + } + ? Tags + : [] + + type GetTagsWithout> = Tags extends [ + infer F, + ...infer R + ] + ? Equal extends true + ? GetTagsWithout + : [F, ...GetTagsWithout] + : [] + + type UnTagAll = Tagged extends keyof IfNeverOrAny + ? B extends infer Origin & { readonly [Tag in Tagged]: GetTags } + ? Origin + : B + : B + + type Tag = [IfNeverOrAny] extends [ + null | undefined + ] + ? B + : UnTagAll & { readonly [Tag in Tagged]: [...GetTags, T] } + + type UnTag< + B, + T extends symbol, + Tags = GetTagsWithout + > = Tagged extends keyof IfNeverOrAny + ? Tags extends [] + ? UnTagAll + : UnTagAll & { readonly [Tag in Tagged]: Tags } + : B + + type HasTag = T extends GetTags[number] + ? true + : false + + type DataList = Record + type PropertyList = Record> + + type PropertyType = + | null + | StringConstructor + | NumberConstructor + | BooleanConstructor + | ArrayConstructor + | ObjectConstructor + | FunctionConstructor + + /** + * PropertyTypeToValueType = any + * PropertyTypeToValueType = string + * PropertyTypeToValueType = number + */ + type PropertyTypeToValueType = T extends null + ? any + : T extends StringConstructor + ? string + : T extends NumberConstructor + ? number + : T extends BooleanConstructor + ? boolean + : T extends ArrayConstructor + ? any[] + : T extends ObjectConstructor + ? Record | null + : T extends FunctionConstructor + ? (...args: any[]) => any + : never + + type Satisfy = V extends T ? V : T + + /** + * PropertyTypeToSimpleValueType = 'foo' + * PropertyTypeToSimpleValueType = string + */ + type PropertyTypeToSimpleValueType< + T extends PropertyType, + V + > = T extends StringConstructor + ? Satisfy + : T extends NumberConstructor + ? Satisfy + : T extends BooleanConstructor + ? Satisfy + : T extends ArrayConstructor + ? Satisfy + : T extends ObjectConstructor + ? Satisfy | null, V> + : T extends FunctionConstructor + ? Satisfy<(...args: any[]) => any, V> + : never + + /** + * PropertyValueType = any + * PropertyValueType = string + * PropertyValueType = string | number + * PropertyValueType<{ type: typeof String }> = string + * PropertyValueType<{ type: typeof String, optionalTypes: [typeof Number] }> = string | number + * PropertyValueType<{ type: typeof String, value: 'foo' }> = 'foo' + * PropertyValueType<{ type: typeof String, value: 123 }> = never + * PropertyValueType<{ type: typeof String, optionalTypes: [typeof Number], value: 123 }> = + * string | 123 + */ + type PropertyValueType

> = + P extends PropertyListItem + ? unknown extends V + ? PropertyTypeToValueType + : ((a: T) => void) extends (a: PropertyType) => void + ? V + : V extends PropertyTypeToValueType + ? PropertyTypeToSimpleValueType + : never + : never + + type PropertyOption = { + type?: T + optionalTypes?: T[] + value?: V + default?: () => V + observer?: ((newValue: V, oldValue: V) => void) | string + comparer?: (newValue: V, oldValue: V) => boolean + reflectIdPrefix?: boolean + } + + type PropertyListItem = T | PropertyOption + + type PropertyValues

= { + [key in keyof P]: PropertyValueType + } + + type DataWithPropertyValues< + TData extends DataList, + TProperty extends PropertyList + > = TData & PropertyValues + + type ComponentMethod = (...args: any[]) => any + + type MethodList = Record + + const METHOD_TAG: unique symbol + + type TaggedMethod = Tag + + type UnTaggedMethod> = UnTag< + M, + typeof METHOD_TAG + > + + type RelationParamsWithKey = { + [name: string]: RelationParams + } + + type TraitRelationParams> = { + target: TraitBehavior.Instance + type: + | 'ancestor' + | 'descendant' + | 'parent' + | 'child' + | 'parent-common-node' + | 'child-common-node' + linked?: (target: Component.TrivialInstance) => void + linkChanged?: (target: Component.TrivialInstance) => void + unlinked?: (target: Component.TrivialInstance) => void + linkFailed?: (target: Component.TrivialInstance) => void + } + + type ChainingFilterFunc< + TAddedFields extends { [key: string]: any }, + TRemovedFields extends string = never + > = ( + chain: Behavior.Builder + ) => Omit & TAddedFields + + type ChainingFilterType = { + add: { [key: string]: any } + remove: string + } + + type ResolveBehaviorBuilder = + IsNever extends false + ? TChainingFilter extends ChainingFilterType + ? Omit & TChainingFilter['add'] + : B + : B + + interface RelationHandler { + list(): TTarget[] + listAsTrait: TOut extends never ? undefined : () => TOut[] + } + + interface RelationHandler { + list(): TTarget[] + listAsTrait: TOut extends never ? undefined : () => TOut[] + } + + type RelationParams = { + target?: + | string + | Component.TrivialComponentType + | Behavior.TrivialInstance + | TraitBehavior.Instance + type: + | 'ancestor' + | 'descendant' + | 'parent' + | 'child' + | 'parent-common-node' + | 'child-common-node' + linked?: (target: Component.TrivialInstance) => void + linkChanged?: (target: Component.TrivialInstance) => void + unlinked?: (target: Component.TrivialInstance) => void + linkFailed?: (target: Component.TrivialInstance) => void + } + + type Lifetimes = { + created: () => void + attached: () => void + moved: () => void + detached: () => void + ready: () => void + } + interface BuilderContext< + TPrevData extends DataList, + TProperty extends PropertyList, + TMethodCaller + > extends ThisType { + self: TMethodCaller + data: Merge> + setData: ( + this: void, + newData: Partial>, + callback?: () => void + ) => void + implement: ( + traitBehavior: TraitBehavior.Instance, + impl: TIn + ) => void + relation( + this: void, + def: TraitRelationParams & ThisType + ): RelationHandler + relation( + this: void, + def: RelationParams & ThisType + ): RelationHandler + observer< + P extends ObserverDataPathStrings< + DataWithPropertyValues + >, + V = GetFromObserverPathString< + DataWithPropertyValues, + P + > + >( + this: void, + paths: P, + func: (newValue: V) => void + ): void + observer< + P extends Array< + ObserverDataPathStrings< + DataWithPropertyValues + > + >, + V = { + [K in keyof P]: GetFromObserverPathString< + DataWithPropertyValues, + P[K] + > + } + >( + this: void, + paths: readonly [...P], + func: (...newValues: V extends any[] ? V : never) => void + ): void + lifetime: ( + this: void, + name: L, + func: Lifetimes[L] + ) => void + pageLifetime: ( + this: void, + name: string, + func: (...args: any[]) => void + ) => void + method: ( + this: void, + func: Fn + ) => TaggedMethod + // listener: (func: EventListener) => TaggedMethod> + } +} diff --git a/types/wx/lib.wx.skyline.d.ts b/types/wx/lib.wx.skyline.d.ts new file mode 100644 index 0000000..e8f5fc8 --- /dev/null +++ b/types/wx/lib.wx.skyline.d.ts @@ -0,0 +1,51 @@ +declare namespace WechatMiniprogram.Skyline { + interface WorkletFunction { + (): T + } + + type Timestamp = number + + type AnimationCallback = ( + finished?: boolean, + current?: AnimatableValue + ) => void + + type AnimatableValue = number | string | number[] + + interface AnimationObject { + callback?: AnimationCallback + current?: AnimatableValue + toValue?: AnimationObject['current'] + startValue?: AnimationObject['current'] + finished?: boolean + strippedCurrent?: number + cancelled?: boolean + + onFrame: (animation: any, timestamp: Timestamp) => boolean + onStart: ( + nextAnimation: any, + current: any, + timestamp: Timestamp, + previousAnimation: any + ) => void + } + + interface SharedValue { + value: T + } + + type DerivedValue = Readonly> + + interface NumericAnimation { + current?: number + } + + type AnimatedStyle = Record + + type FlushOption = 'async' | 'sync' + + interface AnimatedStyleConfig { + immediate: boolean + flush: FlushOption + } +}