Skip to content

Commit

Permalink
Merge pull request #333 from lv-z-l/behavior-ts-support
Browse files Browse the repository at this point in the history
feat: behavior ts type support
  • Loading branch information
SgLy authored Sep 13, 2024
2 parents 0a76c78 + e940cb8 commit 6b8e1d3
Show file tree
Hide file tree
Showing 7 changed files with 182 additions and 25 deletions.
2 changes: 1 addition & 1 deletion test/behavior.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { expectType } from 'tsd'

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

Behavior({
behaviors: [],
Expand Down
7 changes: 4 additions & 3 deletions test/component.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -418,7 +418,7 @@ Component({
},
})

Component<{}, {}, { fn(): void }>({
Component<{}, {}, { fn(): void }, []>({
methods: {
fn() {
expectError(this.notExists)
Expand All @@ -442,7 +442,8 @@ Component<{}, {}, { fn(): void }>({
typeof data,
typeof properties,
/* methods= */{ fn(): string },
/* customProperties= */{},
/* behaviors= */ [],
/* customProperties= */{},
/* isPage= */true
>({
data,
Expand Down Expand Up @@ -496,7 +497,7 @@ Component<{}, {}, { fn(): void }>({
type CustomProperties = {
customProp: string
}
Component<{}, {}, {}, CustomProperties>({
Component<{}, {}, {}, [], CustomProperties>({
lifetimes: {
created() {
this.customProp = 'customProp'
Expand Down
4 changes: 2 additions & 2 deletions test/computed.test.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ declare namespace WechatMiniprogram {
M extends MethodOption,
C extends ComputedOption,
CustomProperty extends IAnyObject = Record<string, never>,
> = Instance<D, P, M, CustomProperty> & { data: ComputedOptionToData<C> }
> = Instance<D, P, M, [], CustomProperty> & { data: ComputedOptionToData<C> }
type ComputedOptions<
D extends DataOption,
P extends PropertyOption,
Expand All @@ -23,7 +23,7 @@ declare namespace WechatMiniprogram {
CustomProperty extends IAnyObject = Record<string, never>,
> = Partial<Computed<C>> &
ThisType<ComputedInstance<D, P, M, C, CustomProperty>> &
Options<D, P, M>
Options<D, P, M, []>
interface ComputedConstructor {
<
D extends DataOption,
Expand Down
6 changes: 3 additions & 3 deletions test/computed.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,12 @@ ComputedComponent({
})

type IMethods = {
_setData: WechatMiniprogram.Component.InstanceMethods<{}>['setData'],
_setData: WechatMiniprogram.Component.InstanceMethods<{}>['setData']
}
type ICustomProperty = {
_originalSetData: WechatMiniprogram.Component.InstanceMethods<{}>['setData'],
_originalSetData: WechatMiniprogram.Component.InstanceMethods<{}>['setData']
}
Behavior<{}, {}, IMethods, ICustomProperty>({
Behavior<{}, {}, IMethods, [], ICustomProperty>({
lifetimes: {
created() {
this._originalSetData = this.setData
Expand Down
117 changes: 115 additions & 2 deletions test/issue.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ wx.request({
f?: () => string
g: () => void
}
type Dialog = WechatMiniprogram.Component.Instance<{}, {}, IDialogMethod>
type Dialog = WechatMiniprogram.Component.Instance<{}, {}, IDialogMethod, []>
Page({
f() {
const comp = this.selectComponent('#comp') as Dialog
Expand Down Expand Up @@ -285,10 +285,11 @@ import WX = WechatMiniprogram
type IMethod = {
setJob(job: string): void
}
type IBehavior = []
type ICustomInstanceProperty = {
job: string
}
Component<IData, IProperty, IMethod, ICustomInstanceProperty>({
Component<IData, IProperty, IMethod, IBehavior, ICustomInstanceProperty>({
properties: {
id: Number,
},
Expand Down Expand Up @@ -458,6 +459,7 @@ import WX = WechatMiniprogram
})
}

// https://github.com/wechat-miniprogram/api-typings/issues/332
{
interface Foo {
f: string
Expand All @@ -481,8 +483,119 @@ import WX = WechatMiniprogram
},
test() {
expectType<Foo>(this.data.bar)
expectType<Foo>(this.properties.bar)
expectType<Foo[]>(this.getData())
},
}
})
}

// https://github.com/wechat-miniprogram/api-typings/issues/332
{
const bA = Behavior({
properties: {
pA: {
type: String,
value: '',
},
pA1: Boolean
},
data: {
dataA: 'init data',
},
methods: {
methodA() {
return this.data.dataA
},
},
})
const bB = Behavior({
properties: {
pB: {
type: Array,
value: [] as string[],
}
},
data: {
dataB: [] as string[],
},
methods: {
methodB() {
return this.data.dataB
},
},
})

Component({
behaviors: [bA, bB],
methods: {
test() {
expectType<string>(this.data.pA)
expectType<boolean>(this.data.pA1)
expectType<string>(this.data.dataA)
expectType<string[]>(this.data.pB)
expectType<string[]>(this.data.dataB)
expectType<string>(this.methodA())
expectType<string[]>(this.methodB())
},
}
})
}
// https://github.com/wechat-miniprogram/api-typings/issues/332#issuecomment-2333434425
{
const b1 = Behavior({
properties: {
pA: {
type: String,
value: '',
},
pA1: Boolean
},
data: {
dataA: 'init data',
},
methods: {
methodA() {
return this.data.dataA
},
},
})
const b2 = Behavior({
behaviors: [b1],
properties: {
pB: {
type: Array,
value: [] as string[],
}
},
data: {
dataB: [] as string[],
},
methods: {
methodB() {
return this.data.dataB
},
test() {
expectType<string>(this.data.pA)
expectType<boolean>(this.data.pA1)
expectType<string>(this.data.dataA)
expectType<string>(this.methodA())
},
},
})

Component({
behaviors: [b2],
methods: {
test() {
expectType<string>(this.data.pA)
expectType<boolean>(this.data.pA1)
expectType<string>(this.data.dataA)
expectType<string[]>(this.data.pB)
expectType<string[]>(this.data.dataB)
expectType<string>(this.methodA())
expectType<string[]>(this.methodB())
},
}
})
}
33 changes: 25 additions & 8 deletions types/wx/lib.wx.behavior.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,47 +21,64 @@ SOFTWARE.
***************************************************************************** */

declare namespace WechatMiniprogram.Behavior {
type BehaviorIdentifier = string

type BehaviorIdentifier<
TData extends DataOption = {},
TProperty extends PropertyOption = {},
TMethod extends MethodOption = {},
TBehavior extends BehaviorOption = []
> = string & {
[key in 'BehaviorType']?: {
data: TData & Component.MixinData<TBehavior>
properties: TProperty & Component.MixinProperties<TBehavior, true>
methods: TMethod & Component.MixinMethods<TBehavior>
}
}
type Instance<
TData extends DataOption,
TProperty extends PropertyOption,
TMethod extends MethodOption,
TBehavior extends BehaviorOption,
TCustomInstanceProperty extends IAnyObject = Record<string, never>
> = Component.Instance<TData, TProperty, TMethod, TCustomInstanceProperty>
type TrivialInstance = Instance<IAnyObject, IAnyObject, IAnyObject>
type TrivialOption = Options<IAnyObject, IAnyObject, IAnyObject>
> = Component.Instance<TData, TProperty, TMethod, TBehavior, TCustomInstanceProperty>
type TrivialInstance = Instance<IAnyObject, IAnyObject, IAnyObject, Component.IEmptyArray>
type TrivialOption = Options<IAnyObject, IAnyObject, IAnyObject, Component.IEmptyArray>
type Options<
TData extends DataOption,
TProperty extends PropertyOption,
TMethod extends MethodOption,
TBehavior extends BehaviorOption,
TCustomInstanceProperty extends IAnyObject = Record<string, never>
> = Partial<Data<TData>> &
Partial<Property<TProperty>> &
Partial<Method<TMethod>> &
Partial<Behavior<TBehavior>> &
Partial<OtherOption> &
Partial<Lifetimes> &
ThisType<Instance<TData, TProperty, TMethod, TCustomInstanceProperty>>
ThisType<Instance<TData, TProperty, TMethod, TBehavior, TCustomInstanceProperty>>
interface Constructor {
<
TData extends DataOption,
TProperty extends PropertyOption,
TMethod extends MethodOption,
TBehavior extends BehaviorOption,
TCustomInstanceProperty extends IAnyObject = Record<string, never>
>(
options: Options<TData, TProperty, TMethod, TCustomInstanceProperty>
): BehaviorIdentifier
options: Options<TData, TProperty, TMethod, TBehavior, TCustomInstanceProperty>
): BehaviorIdentifier<TData, TProperty, TMethod, TBehavior>
}

type DataOption = Component.DataOption
type PropertyOption = Component.PropertyOption
type MethodOption = Component.MethodOption
type BehaviorOption = Component.BehaviorOption
type Data<D extends DataOption> = Component.Data<D>
type Property<P extends PropertyOption> = Component.Property<P>
type Method<M extends MethodOption> = Component.Method<M>
type Behavior<B extends BehaviorOption> = Component.Behavior<B>

type DefinitionFilter = Component.DefinitionFilter
type Lifetimes = Component.Lifetimes

type OtherOption = Omit<Component.OtherOption, 'options'>
}
/** 注册一个 `behavior`,接受一个 `Object` 类型的参数。*/
Expand Down
38 changes: 32 additions & 6 deletions types/wx/lib.wx.component.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,41 +26,51 @@ declare namespace WechatMiniprogram.Component {
TData extends DataOption,
TProperty extends PropertyOption,
TMethod extends Partial<MethodOption>,
TBehavior extends BehaviorOption,
TCustomInstanceProperty extends IAnyObject = {},
TIsPage extends boolean = false
> = InstanceProperties &
InstanceMethods<TData> &
TMethod &
MixinMethods<TBehavior> &
(TIsPage extends true ? Page.ILifetime : {}) &
TCustomInstanceProperty & {
Omit<TCustomInstanceProperty, 'properties' | 'methods' | 'data'> & {
/** 组件数据,**包括内部数据和属性值** */
data: TData & PropertyOptionToData<FilterUnknownProperty<TProperty>>
data: TData & MixinData<TBehavior> &
MixinProperties<TBehavior> & PropertyOptionToData<FilterUnknownProperty<TProperty>>
/** 组件数据,**包括内部数据和属性值**(与 `data` 一致) */
properties: TData & PropertyOptionToData<FilterUnknownProperty<TProperty>>
properties: TData & MixinData<TBehavior> &
MixinProperties<TBehavior> & PropertyOptionToData<FilterUnknownProperty<TProperty>>
}

type IEmptyArray = []
type TrivialInstance = Instance<
IAnyObject,
IAnyObject,
IAnyObject,
IEmptyArray,
IAnyObject
>
type TrivialOption = Options<IAnyObject, IAnyObject, IAnyObject, IAnyObject>
type TrivialOption = Options<IAnyObject, IAnyObject, IAnyObject, IEmptyArray, IAnyObject>
type Options<
TData extends DataOption,
TProperty extends PropertyOption,
TMethod extends MethodOption,
TBehavior extends BehaviorOption,
TCustomInstanceProperty extends IAnyObject = {},
TIsPage extends boolean = false
> = Partial<Data<TData>> &
Partial<Property<TProperty>> &
Partial<Method<TMethod, TIsPage>> &
Partial<Behavior<TBehavior>> &
Partial<OtherOption> &
Partial<Lifetimes> &
ThisType<
Instance<
TData,
TProperty,
TMethod,
TBehavior,
TCustomInstanceProperty,
TIsPage
>
Expand All @@ -70,13 +80,15 @@ declare namespace WechatMiniprogram.Component {
TData extends DataOption,
TProperty extends PropertyOption,
TMethod extends MethodOption,
TBehavior extends BehaviorOption,
TCustomInstanceProperty extends IAnyObject = {},
TIsPage extends boolean = false
>(
options: Options<
TData,
TProperty,
TMethod,
TBehavior,
TCustomInstanceProperty,
TIsPage
>
Expand All @@ -86,6 +98,22 @@ declare namespace WechatMiniprogram.Component {
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 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
type ExtractMethods<T> = T extends { methods: infer M } ? M : never
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (k: infer I) => void ? I : never
type MixinData<T extends any[]> = UnionToIntersection<ExtractData<ExtractBehaviorType<T[number]>>>
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]>>>

interface Behavior<B extends BehaviorOption> {
/** 类似于mixins和traits的组件间代码复用机制,参见 [behaviors](https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/behaviors.html) */
behaviors?: B
}

interface Data<D extends DataOption> {
/** 组件的内部数据,和 `properties` 一同用于组件的模板渲染 */
data?: D
Expand Down Expand Up @@ -524,8 +552,6 @@ declare namespace WechatMiniprogram.Component {
}

interface OtherOption {
/** 类似于mixins和traits的组件间代码复用机制,参见 [behaviors](https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/behaviors.html) */
behaviors: Behavior.BehaviorIdentifier[]
/**
* 组件数据字段监听器,用于监听 properties 和 data 的变化,参见 [数据监听器](https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/observer.html)
*
Expand Down

0 comments on commit 6b8e1d3

Please sign in to comment.