-
-
Notifications
You must be signed in to change notification settings - Fork 9.3k
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
Remove all ts-ignore in vue, tighten typings #11547
Conversation
@@ -3,7 +3,6 @@ | |||
"compilerOptions": { | |||
"rootDir": "./src" | |||
}, | |||
"include": ["src/**/*", "typings.d.ts"], | |||
"exclude": ["src/**.test.ts"], | |||
"files": ["./typings.d.ts"] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seemed redundant since typings.d.ts
was included with include
already.
@@ -0,0 +1,2 @@ | |||
import start from './dist/client/index'; | |||
export = start; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Adding typings to client.js
here caused all the apps to go from import { start } from '@storybook/core/client'
to import client from '@storybook/core/client'
.
However, the actual implementation of core/client.js
, core/src/index.js
, and core/src/preview/index.ts
matches this, as core/src/client/preview/index.ts
exports an object as default
instead of exporting multiple functions. Therefore to match that, has to be default-imported in typescript.
core/src/client/preview/index.ts
could be changed to a more correct module, but I'm not sure if there was a reason it was done the way it is right now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure to understand why import { start } from '@storybook/core/client'
need to become import client from '@storybook/core/client'
. I added locally this lib/core/client.d.ts
and has no error with import { start } from '@storybook/core/client'
.
Should start
not be client
instead as start
is only a property of the exported object?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's strange that it worked fine for you @gaetanmaisse - typescript was definitely erroring for me when I kept it as import { start }
.
You're right that it works fine once it compiles down to CommonJS, as it's basically const { start } = require('@storybook/client-api')
either way, but it's not valid in ES modules or typescript to import a single property of a default-exported object. Babel fudges this restriction a bit, when interfacing with CommonJS code.
For correct ES modules, we would need to have:
core/src/client/preview/index.ts
export { ClientApi, StoryStore, ConfigApi } from '@storybook/client-api';
export { toId } from '@storybook/csf';
export { default as start } from './start';
core/src/client/index.ts
export * from './preview';
export * from './preview/types';
core/client.d.ts
export * from './dist/client';
Which is definitely simpler, but I didn't know if there was a reason for the current way, so I didn't touch that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 thanks for the clarification, I agree with you it will be way simpler/clearer with the solution in your comment. I had the same question about the reason of this kind of export in other parts of Storybook but I never had a strong answer, so it's surely a code style legacy.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll go ahead and switch to the new style and hope nothing breaks 😄
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is really bizzare, yarn build core
works, yarn build react
works, yarn build core api react vue
works, but yarn build --all
fails when building @storybook/react
. Is there anything funky going on when I run --all
@gaetanmaisse?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm I would say no, but maybe 🙈 😂 Is it working with yarn bootstrap --core
?
@@ -95,7 +95,7 @@ export interface GetStorybookKind { | |||
|
|||
// This really belongs in lib/core, but that depends on lib/ui which (dev) depends on app/react | |||
// which needs this type. So we put it here to avoid the circular dependency problem. | |||
export type RenderContext = StoreItem & { | |||
export type RenderContext<StoryFnReturnType = unknown> = StoreItem<StoryFnReturnType> & { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This required some changes in the other apps besides Vue, but now they all get the correct type from calling storyFn()
instead of any
, so it's a win in my opinion. It's technically breaking if there are any other apps out there, that expect any
instead of unknown
? unknown
is generally more type-safe though.
"resolveJsonModule": true | ||
"resolveJsonModule": true, | ||
"strict": true, | ||
"noImplicitReturns": true |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Made the Vue app as strict as I could for typescript, to catch everything, can revert these if wanted.
{ | ||
files: ['*.ts', '*.tsx'], | ||
rules: { | ||
'@typescript-eslint/explicit-function-return-type': 'error', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Made the Vue app as strict as I could for typescript, to catch everything, can revert these if wanted.
// known limitation: we don't have the component instance to pass | ||
return def.call(); | ||
return (validator.default as () => T | null | undefined)(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tried as I might, I couldn't get this to be completely type-safe using a type guard. I think this would need negated types to remove T & Function
from the possible types.
// TODO: some vue expert needs to look at this | ||
export type StoryFnVueReturnType = string | Component; | ||
/** Any options that can be passed into Vue.extend */ | ||
export type VueOptions = Exclude<Parameters<VueConstructor['extend']>[0], undefined>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I could not find a union of all options interfaces in the Vue typings, pulling all possible arguments to extend
gets them all quite easily. The types are very long otherwise. Let me know if you know a better type.
@@ -5,7 +5,6 @@ export { | |||
addParameters, | |||
configure, | |||
getStorybook, | |||
forceReRender, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm guessing this was an old field - not present any more once type-safety was added.
const element = storyFn(); | ||
|
||
if (!element) { | ||
showError({ | ||
title: `Expecting an Aurelia component from the story: "${selectedStory}" of "${selectedKind}".`, | ||
title: `Expecting an Aurelia component from the story.`, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Newer way to get the story name? I didn't look too far into it.
a985e88
to
64373e2
Compare
package.json
Outdated
@@ -240,7 +240,7 @@ | |||
"ts-dedent": "^1.1.1", | |||
"ts-jest": "^26.1.0", | |||
"ts-node": "^8.9.1", | |||
"typescript": "^3.4.0", | |||
"typescript": "^3.7.0", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typescript 3.9 is already pinned in the yarn.lock
, but since I used optional chaining which requires at least typescript 3.7, I figured this should be changed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you can set ^3.9.6
directly because as you said we are already using it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fair enough!
@gaetanmaisse it looks like there are a lot of duplicates in yarn.lock
(like, typescript
shows up 15 times in yarn why typescript
), it might be a good idea to run yarn-deduplicate
to clean that up.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
True! I plan to do a big dep clean and dedup work in Storybook 6.1 :)
|
||
function prepare(rawStory: undefined, innerStory?: VueConstructor): undefined; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A little bit awkward, but maintains the type-safety effectively, since prepare
will only return undefined
if rawStory === undefined
, which won't happen if someone uses typescript, but may happen otherwise.
@@ -28,7 +28,7 @@ const setRenderFetchAndConfigure: ClientApi['configure'] = (loader, module, opti | |||
if (options && options.fetchStoryHtml) { | |||
setFetchStoryHtml(options.fetchStoryHtml); | |||
} | |||
api.configure(loader, module, framework); | |||
api.configure(framework, loader, module); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This appeared to be just wrong? Was revealed by the type-safety
@abrenneke This looks amazing, great work!!! @backbone87 can you please take a look? |
Less any, more explicit types and type checks Turned on noImplicitReturns, strict, explicit-function-return-type (vue)
64373e2
to
49f4a3a
Compare
@abrenneke I will take a second look asap 😉 |
What I did
Well while learning how the Vue integration works for fixing unrelated issues I went down the typescript rabbit-hole and ended up improving the typings for Vue, and a little bit for all of the apps.
@ts-ignore
and mosttodo
are removedany
in the codeStoryFnVueReturnType
is nowstring | VueConstructor | VueOptions
, whereVueOptions
is any argument thatVue.extend()
accepts.Some of the changes (such as to
@storybook/client-api
and@storybook/core
) leaked out of the changes here and into the other apps. Those changes enhance type-safety across the board, so in my opinion they're good to have. I'll add comments to point them out.@backbone87 reviewed #7578 and I got a lot of insight from those comments so maybe they would want to look this over.
All tests, builds, & linting passing (on my machine at least).
How to test
Existing tests cover the changes
Nope
Nope