Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions test/behavior.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { expectType } from 'tsd'
import { expectAssignable, expectType } from 'tsd'

expectType<string & {}>(Behavior({}))
expectAssignable<string>(Behavior({}))

Behavior({
behaviors: [],
Expand Down
36 changes: 34 additions & 2 deletions test/component.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { expectType, expectError, expectNotAssignable } from 'tsd'
import { expectType, expectError, expectNotAssignable, expectAssignable } from 'tsd'

expectType<string>(Component({}))
expectAssignable<string>(Component({}))

Component({
behaviors: [''],
Expand Down Expand Up @@ -511,3 +511,35 @@ Component<{}, {}, { fn(): void }, []>({
})
}

{
const def = Component({
properties: {
a: Boolean,
},
data: {
b: 1,
},
methods: {
c() {}
},
})
type FieldTypes = (typeof def)['_$fieldTypes']
expectType<FieldTypes['propertyValues']['a']>(false as boolean)
expectType<FieldTypes['dataWithProperties']['a']>(false as boolean)
expectType<FieldTypes['dataWithProperties']['b']>(1 as number)
expectType<FieldTypes['methods']['c']>(() => {})
}

{
type CustomProperties = {
customProp: string
}
Component<{}, {}, {}, [], CustomProperties>({
lifetimes: {
created() {
this.customProp = 'customProp'
}
}
})
}

18 changes: 15 additions & 3 deletions test/page.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { expectType, expectError, expectNotAssignable } from 'tsd'
import { expectType, expectError, expectNotAssignable, expectAssignable } from 'tsd'

expectType<void>(Page({}))
expectAssignable<void>(Page({}))

expectType<Record<string, any>>(getCurrentPages()[0].data)

Expand Down Expand Up @@ -241,4 +241,16 @@ Page({
onShareAppMessage(): WechatMiniprogram.Page.ICustomShareContent {
return { title: this.data.a, imageUrl: '', path: '' }
},
})
})

{
const def = Page({
data: {
b: 1,
},
c() {},
})
type FieldTypes = (typeof def)['_$fieldTypes']
expectType<FieldTypes['dataWithProperties']['b']>(1 as number)
expectType<FieldTypes['methods']['c']>(() => {})
}
30 changes: 22 additions & 8 deletions types/wx/lib.wx.behavior.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,7 @@ declare namespace WechatMiniprogram.Behavior {
TProperty extends PropertyOption = {},
TMethod extends MethodOption = {},
TBehavior extends BehaviorOption = []
> = string & {
[key in 'BehaviorType']?: {
data: Component.FilterUnknownType<TData> & Component.MixinData<TBehavior>
properties: Component.FilterUnknownType<TProperty> & Component.MixinProperties<TBehavior, true>
methods: Component.FilterUnknownType<TMethod> & Component.MixinMethods<TBehavior>
}
}
> = string & BehaviorTypeSignature<TData, TProperty, TMethod, TBehavior>
type Instance<
TData extends DataOption,
TProperty extends PropertyOption,
Expand Down Expand Up @@ -65,7 +59,7 @@ declare namespace WechatMiniprogram.Behavior {
TCustomInstanceProperty extends IAnyObject = Record<string, never>
>(
options: Options<TData, TProperty, TMethod, TBehavior, TCustomInstanceProperty>
): BehaviorIdentifier<TData, TProperty, TMethod, TBehavior>
): string & BehaviorTypeSignature<TData, TProperty, TMethod, TBehavior>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

keep using BehaviorIdentifier should be fine?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll fix it later

}

type DataOption = Component.DataOption
Expand All @@ -80,6 +74,26 @@ declare namespace WechatMiniprogram.Behavior {
type DefinitionFilter = Component.DefinitionFilter
type Lifetimes = Component.Lifetimes
type OtherOption = Omit<Component.OtherOption, 'options'>

/** 用于辅助识别 behavior 字段类型的虚拟字段 */
class BehaviorTypeSignature<
TData extends DataOption,
TProperty extends PropertyOption,
TMethod extends MethodOption,
TBehavior extends BehaviorOption,
> {
protected readonly _$behaviorFieldTypes?: BehaviorTypeSignatureFields<TData, TProperty, TMethod, TBehavior>
}
type BehaviorTypeSignatureFields<
TData extends DataOption,
TProperty extends PropertyOption,
TMethod extends MethodOption,
TBehavior extends BehaviorOption,
> = {
data: Component.FilterUnknownType<TData> & Component.MixinData<TBehavior>
properties: Component.FilterUnknownType<TProperty> & Component.MixinProperties<TBehavior, true>
methods: Component.FilterUnknownType<TMethod> & Component.MixinMethods<TBehavior>
}
}
/** 注册一个 `behavior`,接受一个 `Object` 类型的参数。*/
declare let Behavior: WechatMiniprogram.Behavior.Constructor
20 changes: 18 additions & 2 deletions types/wx/lib.wx.component.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,14 +94,17 @@ declare namespace WechatMiniprogram.Component {
TCustomInstanceProperty,
TIsPage
>
): string
): string & ComponentTypeSignature<TData, TProperty, TMethod>
}
type DataOption = Record<string, any>
type PropertyOption = Record<string, AllProperty>
type MethodOption = Record<string, Function>

type BehaviorOption = Behavior.BehaviorIdentifier[]
type ExtractBehaviorType<T> = T extends { BehaviorType?: infer B } ? B : never
type BehaviorFieldTypes<T> = (T & Record<string, unknown>)['_$behaviorFieldTypes']
type ExtractBehaviorType<T> = BehaviorFieldTypes<T> extends Behavior.BehaviorTypeSignatureFields<any, any, any, any> | undefined
? BehaviorFieldTypes<T>
: never
type ExtractData<T> = T extends { data: infer D } ? D : never
type ExtractProperties<T, TIsBehavior extends boolean = false> = T extends { properties: infer P } ?
TIsBehavior extends true ? P : PropertyOptionToData<P extends PropertyOption ? P : {}> : never
Expand All @@ -111,6 +114,19 @@ declare namespace WechatMiniprogram.Component {
type MixinProperties<T extends any[], TIsBehavior extends boolean = false> = UnionToIntersection<ExtractProperties<ExtractBehaviorType<T[number]>, TIsBehavior>>
type MixinMethods<T extends any[]> = UnionToIntersection<ExtractMethods<ExtractBehaviorType<T[number]>>>

/** 用于辅助识别组件类型的虚拟字段(供 glass-easel-analyzer 等外部模块使用) */
class ComponentTypeSignature<
TData extends DataOption,
TProperty extends PropertyOption,
TMethod extends MethodOption,
> {
protected readonly _$fieldTypes: {
propertyValues: PropertyOptionToData<TProperty>
dataWithProperties: TData & PropertyOptionToData<TProperty>
methods: TMethod
}
}

interface Behavior<B extends BehaviorOption> {
/** 类似于mixins和traits的组件间代码复用机制,参见 [behaviors](https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/behaviors.html) */
behaviors?: B
Expand Down
6 changes: 5 additions & 1 deletion types/wx/lib.wx.page.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,11 @@ declare namespace WechatMiniprogram.Page {
interface Constructor {
<TData extends DataOption, TCustom extends CustomOption>(
options: Options<TData, TCustom>
): void
): void & Component.ComponentTypeSignature<
TData,
Record<never, never>,
{ [K in keyof TCustom as TCustom[K] extends Function ? K : never]: TCustom[K] }
>
}
interface ILifetime {
/** 生命周期回调—监听页面加载
Expand Down