Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(runtime-core): mixins/extends support in TypeScript #626

Merged
merged 35 commits into from
Jun 9, 2020
Merged

feat(runtime-core): mixins/extends support in TypeScript #626

merged 35 commits into from
Jun 9, 2020

Conversation

dolymood
Copy link
Contributor

@dolymood dolymood commented Jan 16, 2020

We want to support mixins/extends options in TS.

Include changes:

  • Return type of defineComponent: {new(): ComponentPublicInstance} -> {new(): ComponentPublicInstance} & (ComponentOptionsWithoutProps| ComponentOptionsWithArrayProps| ComponentOptionsWithObjectProps)
  • Overloads defineComponent and ComponentOptionsWithoutProps,ComponentOptionsWithArrayProps,ComponentOptionsWithObjectProps and ComponentOptionsBase, LegacyOptions : add new type variable Mixin which should be defined by defineComponent
  • Breaking changes about h(): split h(type: ComponentOptionsWithoutProps|ComponentOptionsWithArrayProps) because ComponentOptionsWithArrayProps/ComponentOptionsWithObjectProps extends ComponentOptionsWithoutProps will be true. If the props can not match the ComponentOptionsWithArrayProps, it will be match the ComponentOptionsWithoutProps cases with error type props.

@dolymood dolymood changed the title feat(runtime-core): mixin support in TypeScript feat(runtime-core): mixins/extends support in TypeScript Jan 16, 2020
@yyx990803
Copy link
Member

Since this is a pretty substantial PR, please be patient it takes some time for us to review it.

Copy link
Member

@pikax pikax left a comment

Choose a reason for hiding this comment

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

Good job, seems to be working, it would be great if you could merge master into this branch to a more in-depth review 👍

mixins?: LegacyComponent[]
extends?: LegacyComponent
mixins?: Mixin[]
extends?: Mixin
Copy link
Member

Choose a reason for hiding this comment

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

extends can't be Mixin because if this typing enforces extends to be from type declared in the mixin

// works
 mixins: [mixinA, mixinB, mixinC],
 extends: mixinB,

//doesn't work
mixins: [mixinA, mixinB],
extends: mixinC,

@dolymood
Copy link
Contributor Author

@pikax Thanks for your first review. I've merged vue-next master codes and fixed extends can't be Mixin problem. Unit tests https://github.com/vuejs/vue-next/pull/626/files#diff-7075cde1ccaf65deaa20bcce5d00eed2R558 , https://github.com/vuejs/vue-next/pull/626/files#diff-3764072b8e3b52266b03966f08a8c2d9R480

Copy link
Member

@pikax pikax left a comment

Choose a reason for hiding this comment

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

I will go through the typescript next, just need more ☕ before it 😄 , might take couple days

EDIT: looking good so far! Thank you for the quick merge!

expect(this.c).toBe(3)
// should not visit other props/data... in one mixin
// ????? only test current mixin props/data
// expect(this.b).toBe(2)
Copy link
Member

Choose a reason for hiding this comment

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

This test is quite important, because is expected you to access the parent props in the mixin.

But with typed mixins it makes quite hard to get those types using defineComponent(maybe another api defineMixin<vmProps>?).

I think this test would be better if you remove defineComponent from the mixins and call it in the Comp mixin

const mixinA = {
// ...
}

const mixinB = {
//...
}

// etc

const Comp = defineComponent({
  mixins: [
    defineComponent(mixinA),
    defineComponent(mixinB),
    defineComponent(mixinC)
  ],
// etc
)

Or having it has two tests, one defining typed based other using non-typed (no-defineComponent)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Very good suggestion! 👍
Mixin types was tested in defineComponent-test-d.tsx for types cases.
Here we must ensure the runtime logic is correct.


expect(renderToString(h(Comp))).toBe(`12`)
expect(calls).toEqual(['base', 'comp'])
})

test('extends with mixins', () => {
Copy link
Member

Choose a reason for hiding this comment

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

Same as the previous component, we probably want to test the access of the component who's extending.

@pikax
Copy link
Member

pikax commented May 1, 2020

@dolymood Thank you for the changes!

It looks good so far (went through most of the code), but unfortunately the current typings for defineComponent won't work with Typescript 3.9-rc.

Types returned by

ComponentPublicInstanceConstructor<
  CreateComponentPublicInstance<
    ExtractPropTypes<PropsOptions>,
    RawBindings,
    D,
    C,
    M,
    Mixin,
    Extends,
    E,
    VNodeProps & ExtractPropTypes<PropsOptions, false>
  >
> &
  ComponentOptionsWithObjectProps<
    PropsOptions,
    RawBindings,
    D,
    C,
    M,
    Mixin,
    Extends,
    E,
    EE
  >

Will be never as you can see here(this PR with TS 3.9rc)

image

Manage to make it build & pass all the tests by assigning an interface to the defineComponent instead of ComponentOptions* as seen here (WIP)

I think we should bump to TS 3.9 before we merge this PR :/

Sorry @dolymood to keep asking you to change this PR 🙏

Suggestions for 3.9 are very welcome :)

@dolymood
Copy link
Contributor Author

dolymood commented May 3, 2020

Bump to TS 3.9 is very necessary. I will try to update codes to compatible with TS 3.9.

# Conflicts:
#	packages/runtime-core/src/apiDefineComponent.ts
@ktsn
Copy link
Member

ktsn commented May 4, 2020

Regarding TS 3.9 issue, looks like we are affected by "Intersections Reduced By Discriminant Properties" breaking change.
https://devblogs.microsoft.com/typescript/announcing-typescript-3-9-rc/

Looks like the culprit property is call type in ComponentOptionsBase.
https://github.com/vuejs/vue-next/blob/master/packages/runtime-core/src/componentOptions.ts#L137

I'm not sure how this property type work in Vue typings but changing its type to bottom function type seems to solve the issue.

call?: (this: unknown, ...args: unknown[]) => never

pikax added 2 commits May 4, 2020 16:50
This reverts commit 11cd53d.

# Conflicts:
#	packages/runtime-core/src/apiDefineComponent.ts
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants