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

fix(types): untyped components, test more edge cases #5713

Merged
merged 3 commits into from
Jan 14, 2024
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
12 changes: 6 additions & 6 deletions flake.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
gitMinimal

nodejs_20
corepack_20

# Qwik optimizer deps
wasm-pack
Expand Down
2 changes: 1 addition & 1 deletion packages/docs/src/routes/api/qwik-city/api.json
Original file line number Diff line number Diff line change
Expand Up @@ -659,7 +659,7 @@
}
],
"kind": "Variable",
"content": "```typescript\nRouterOutlet: import(\"@builder.io/qwik\").Component<Record<any, any>>\n```",
"content": "```typescript\nRouterOutlet: import(\"@builder.io/qwik\").Component<unknown>\n```",
"editUrl": "https://github.com/BuilderIO/qwik/tree/main/packages/qwik-city/runtime/src/router-outlet-component.tsx",
"mdFile": "qwik-city.routeroutlet.md"
},
Expand Down
2 changes: 1 addition & 1 deletion packages/docs/src/routes/api/qwik-city/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -705,7 +705,7 @@ export type RouteNavigate = QRL<
## RouterOutlet

```typescript
RouterOutlet: import("@builder.io/qwik").Component<Record<any, any>>;
RouterOutlet: import("@builder.io/qwik").Component<unknown>;
```

[Edit this section](https://github.com/BuilderIO/qwik/tree/main/packages/qwik-city/runtime/src/router-outlet-component.tsx)
Expand Down
16 changes: 8 additions & 8 deletions packages/docs/src/routes/api/qwik/api.json
Original file line number Diff line number Diff line change
Expand Up @@ -460,7 +460,7 @@
}
],
"kind": "TypeAlias",
"content": "Type representing the Qwik component.\n\n`Component` is the type returned by invoking `component$`<!-- -->.\n\n```tsx\ninterface MyComponentProps {\n someProp: string;\n}\nconst MyComponent: Component<MyComponentProps> = component$((props: MyComponentProps) => {\n return <span>{props.someProp}</span>;\n});\n```\n\n\n```typescript\nexport type Component<PROPS extends Record<any, any> = Record<string, unknown>> = FunctionComponent<PublicProps<PROPS>>;\n```\n**References:** [FunctionComponent](#functioncomponent)<!-- -->, [PublicProps](#publicprops)",
"content": "Type representing the Qwik component.\n\n`Component` is the type returned by invoking `component$`<!-- -->.\n\n```tsx\ninterface MyComponentProps {\n someProp: string;\n}\nconst MyComponent: Component<MyComponentProps> = component$((props: MyComponentProps) => {\n return <span>{props.someProp}</span>;\n});\n```\n\n\n```typescript\nexport type Component<PROPS = unknown> = FunctionComponent<PublicProps<PROPS>>;\n```\n**References:** [FunctionComponent](#functioncomponent)<!-- -->, [PublicProps](#publicprops)",
"editUrl": "https://github.com/BuilderIO/qwik/tree/main/packages/qwik/src/core/component/component.public.ts",
"mdFile": "qwik.component.md"
},
Expand All @@ -474,7 +474,7 @@
}
],
"kind": "Variable",
"content": "Declare a Qwik component that can be used to create UI.\n\nUse `component$` to declare a Qwik component. A Qwik component is a special kind of component that allows the Qwik framework to lazy load and execute the component independently of other Qwik components as well as lazy load the component's life-cycle hooks and event handlers.\n\nSide note: You can also declare regular (standard JSX) components that will have standard synchronous behavior.\n\nQwik component is a facade that describes how the component should be used without forcing the implementation of the component to be eagerly loaded. A minimum Qwik definition consists of:\n\n\\#\\#\\# Example\n\nAn example showing how to create a counter component:\n\n```tsx\nexport interface CounterProps {\n initialValue?: number;\n step?: number;\n}\nexport const Counter = component$((props: CounterProps) => {\n const state = useStore({ count: props.initialValue || 0 });\n return (\n <div>\n <span>{state.count}</span>\n <button onClick$={() => (state.count += props.step || 1)}>+</button>\n </div>\n );\n});\n```\n- `component$` is how a component gets declared. - `{ value?: number; step?: number }` declares the public (props) interface of the component. - `{ count: number }` declares the private (state) interface of the component.\n\nThe above can then be used like so:\n\n```tsx\nexport const OtherComponent = component$(() => {\n return <Counter initialValue={100} />;\n});\n```\nSee also: `component`<!-- -->, `useCleanup`<!-- -->, `onResume`<!-- -->, `onPause`<!-- -->, `useOn`<!-- -->, `useOnDocument`<!-- -->, `useOnWindow`<!-- -->, `useStyles`\n\n\n```typescript\ncomponent$: <PROPS extends Record<any, any>>(onMount: (props: PROPS) => JSXNode | null) => Component<PROPS>\n```",
"content": "Declare a Qwik component that can be used to create UI.\n\nUse `component$` to declare a Qwik component. A Qwik component is a special kind of component that allows the Qwik framework to lazy load and execute the component independently of other Qwik components as well as lazy load the component's life-cycle hooks and event handlers.\n\nSide note: You can also declare regular (standard JSX) components that will have standard synchronous behavior.\n\nQwik component is a facade that describes how the component should be used without forcing the implementation of the component to be eagerly loaded. A minimum Qwik definition consists of:\n\n\\#\\#\\# Example\n\nAn example showing how to create a counter component:\n\n```tsx\nexport interface CounterProps {\n initialValue?: number;\n step?: number;\n}\nexport const Counter = component$((props: CounterProps) => {\n const state = useStore({ count: props.initialValue || 0 });\n return (\n <div>\n <span>{state.count}</span>\n <button onClick$={() => (state.count += props.step || 1)}>+</button>\n </div>\n );\n});\n```\n- `component$` is how a component gets declared. - `{ value?: number; step?: number }` declares the public (props) interface of the component. - `{ count: number }` declares the private (state) interface of the component.\n\nThe above can then be used like so:\n\n```tsx\nexport const OtherComponent = component$(() => {\n return <Counter initialValue={100} />;\n});\n```\nSee also: `component`<!-- -->, `useCleanup`<!-- -->, `onResume`<!-- -->, `onPause`<!-- -->, `useOn`<!-- -->, `useOnDocument`<!-- -->, `useOnWindow`<!-- -->, `useStyles`\n\n\n```typescript\ncomponent$: <PROPS = unknown>(onMount: OnRenderFn<PROPS>) => Component<PROPS>\n```",
"editUrl": "https://github.com/BuilderIO/qwik/tree/main/packages/qwik/src/core/component/component.public.ts",
"mdFile": "qwik.component_.md"
},
Expand Down Expand Up @@ -730,7 +730,7 @@
}
],
"kind": "TypeAlias",
"content": "```typescript\ntype ElementType = string | FunctionComponent;\n```\n**References:** [FunctionComponent](#functioncomponent)",
"content": "```typescript\ntype ElementType = string | FunctionComponent<Record<any, any>>;\n```\n**References:** [FunctionComponent](#functioncomponent)",
"mdFile": "qwik.qwikjsx.elementtype.md"
},
{
Expand Down Expand Up @@ -855,7 +855,7 @@
}
],
"kind": "TypeAlias",
"content": "Any sync or async function that returns JSXOutput.\n\nNote that this includes QRLs.\n\nThe `key`<!-- -->, `flags` and `dev` parameters are for internal use.\n\n\n```typescript\nexport type FunctionComponent<P extends Record<any, any> = Record<any, any>> = {\n renderFn(props: P, key: string | null, flags: number, dev?: DevJSX): JSXOutput | Promise<JSXOutput>;\n}['renderFn'];\n```\n**References:** [DevJSX](#devjsx)<!-- -->, [JSXOutput](#jsxoutput)",
"content": "Any sync or async function that returns JSXOutput.\n\nNote that this includes QRLs.\n\nThe `key`<!-- -->, `flags` and `dev` parameters are for internal use.\n\n\n```typescript\nexport type FunctionComponent<P extends Record<any, any> = Record<any, unknown>> = {\n renderFn(props: P, key: string | null, flags: number, dev?: DevJSX): JSXOutput | Promise<JSXOutput>;\n}['renderFn'];\n```\n**References:** [DevJSX](#devjsx)<!-- -->, [JSXOutput](#jsxoutput)",
"editUrl": "https://github.com/BuilderIO/qwik/tree/main/packages/qwik/src/core/render/jsx/types/jsx-node.ts",
"mdFile": "qwik.functioncomponent.md"
},
Expand Down Expand Up @@ -1222,7 +1222,7 @@
}
],
"kind": "Interface",
"content": "```typescript\nexport interface JSXNode<T = string | FunctionComponent> \n```\n\n\n| Property | Modifiers | Type | Description |\n| --- | --- | --- | --- |\n| [children](#) | | [JSXChildren](#jsxchildren) \\| null | |\n| [dev?](#) | | [DevJSX](#devjsx) | _(Optional)_ |\n| [flags](#) | | number | |\n| [immutableProps](#) | | Record&lt;any, unknown&gt; \\| null | |\n| [key](#) | | string \\| null | |\n| [props](#) | | T extends [FunctionComponent](#functioncomponent)<!-- -->&lt;infer B&gt; ? B : Record&lt;any, unknown&gt; | |\n| [type](#) | | T | |",
"content": "```typescript\nexport interface JSXNode<T extends string | FunctionComponent | unknown = unknown> \n```\n\n\n| Property | Modifiers | Type | Description |\n| --- | --- | --- | --- |\n| [children](#) | | [JSXChildren](#jsxchildren) \\| null | |\n| [dev?](#) | | [DevJSX](#devjsx) | _(Optional)_ |\n| [flags](#) | | number | |\n| [immutableProps](#) | | Record&lt;any, unknown&gt; \\| null | |\n| [key](#) | | string \\| null | |\n| [props](#) | | T extends [FunctionComponent](#functioncomponent)<!-- -->&lt;infer B&gt; ? B : Record&lt;any, unknown&gt; | |\n| [type](#) | | T | |",
"editUrl": "https://github.com/BuilderIO/qwik/tree/main/packages/qwik/src/core/render/jsx/types/jsx-node.ts",
"mdFile": "qwik.jsxnode.md"
},
Expand Down Expand Up @@ -1642,7 +1642,7 @@
}
],
"kind": "TypeAlias",
"content": "```typescript\nexport type OnRenderFn<PROPS extends Record<any, any>> = (props: PROPS) => JSXNode | null;\n```\n**References:** [JSXNode](#jsxnode)",
"content": "```typescript\nexport type OnRenderFn<PROPS> = (props: PROPS) => JSXOutput;\n```\n**References:** [JSXOutput](#jsxoutput)",
"editUrl": "https://github.com/BuilderIO/qwik/tree/main/packages/qwik/src/core/component/component.public.ts",
"mdFile": "qwik.onrenderfn.md"
},
Expand Down Expand Up @@ -1810,7 +1810,7 @@
}
],
"kind": "TypeAlias",
"content": "Infers `Props` from the component or tag.\n\n\n```typescript\nexport type PropsOf<COMP> = COMP extends string ? COMP extends keyof QwikIntrinsicElements ? QwikIntrinsicElements[COMP] : QwikIntrinsicElements['span'] : NonNullable<COMP> extends never ? never : COMP extends FunctionComponent<infer PROPS> ? NonNullable<PROPS> : Record<string, unknown>;\n```\n**References:** [QwikIntrinsicElements](#qwikintrinsicelements)<!-- -->, [FunctionComponent](#functioncomponent)\n\n\n\n```tsx\nconst Desc = component$(({desc, ...props}: { desc: string } & PropsOf<'div'>) => {\n return <div {...props}>{desc}</div>;\n});\n\nconst TitleBox = component$(({title, ...props}: { title: string } & PropsOf<Box>) => {\n return <Box {...props}><h1>{title}</h1></Box>;\n});\n```",
"content": "Infers `Props` from the component or tag.\n\n\n```typescript\nexport type PropsOf<COMP> = COMP extends string ? COMP extends keyof QwikIntrinsicElements ? QwikIntrinsicElements[COMP] : QwikIntrinsicElements['span'] : NonNullable<COMP> extends never ? never : COMP extends FunctionComponent<infer PROPS> ? PROPS extends Record<any, infer V> ? IsAny<V> extends true ? never : ObjectProps<PROPS> : COMP extends Component<infer OrigProps> ? ObjectProps<OrigProps> : PROPS : never;\n```\n**References:** [QwikIntrinsicElements](#qwikintrinsicelements)<!-- -->, [FunctionComponent](#functioncomponent)<!-- -->, [Component](#component)\n\n\n\n```tsx\nconst Desc = component$(({desc, ...props}: { desc: string } & PropsOf<'div'>) => {\n return <div {...props}>{desc}</div>;\n});\n\nconst TitleBox = component$(({title, ...props}: { title: string } & PropsOf<Box>) => {\n return <Box {...props}><h1>{title}</h1></Box>;\n});\n```",
"editUrl": "https://github.com/BuilderIO/qwik/tree/main/packages/qwik/src/core/component/component.public.ts",
"mdFile": "qwik.propsof.md"
},
Expand All @@ -1824,7 +1824,7 @@
}
],
"kind": "TypeAlias",
"content": "Extends the defined component PROPS, adding the default ones (children and q:slot) and allowing plain functions to QRL arguments.\n\n\n```typescript\nexport type PublicProps<PROPS extends Record<any, any>> = Omit<PROPS, `${string}$`> & _Only$<PROPS> & ComponentBaseProps & ComponentChildren<PROPS>;\n```\n**References:** [ComponentBaseProps](#componentbaseprops)",
"content": "Extends the defined component PROPS, adding the default ones (children and q:slot) and allowing plain functions to QRL arguments.\n\n\n```typescript\nexport type PublicProps<PROPS> = (PROPS extends Record<any, any> ? Omit<PROPS, `${string}$`> & _Only$<PROPS> : unknown extends PROPS ? {} : PROPS) & ComponentBaseProps & ComponentChildren<PROPS>;\n```\n**References:** [ComponentBaseProps](#componentbaseprops)",
"editUrl": "https://github.com/BuilderIO/qwik/tree/main/packages/qwik/src/core/component/component.public.ts",
"mdFile": "qwik.publicprops.md"
},
Expand Down
44 changes: 23 additions & 21 deletions packages/docs/src/routes/api/qwik/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -404,9 +404,7 @@ const MyComponent: Component<MyComponentProps> = component$(
```

```typescript
export type Component<
PROPS extends Record<any, any> = Record<string, unknown>,
> = FunctionComponent<PublicProps<PROPS>>;
export type Component<PROPS = unknown> = FunctionComponent<PublicProps<PROPS>>;
```

**References:** [FunctionComponent](#functioncomponent), [PublicProps](#publicprops)
Expand Down Expand Up @@ -456,9 +454,7 @@ export const OtherComponent = component$(() => {
See also: `component`, `useCleanup`, `onResume`, `onPause`, `useOn`, `useOnDocument`, `useOnWindow`, `useStyles`

```typescript
component$: <PROPS extends Record<any, any>>(
onMount: (props: PROPS) => JSXNode | null,
) => Component<PROPS>;
component$: <PROPS = unknown>(onMount: OnRenderFn<PROPS>) => Component<PROPS>;
```

[Edit this section](https://github.com/BuilderIO/qwik/tree/main/packages/qwik/src/core/component/component.public.ts)
Expand Down Expand Up @@ -782,7 +778,7 @@ interface ElementChildrenAttribute
## ElementType

```typescript
type ElementType = string | FunctionComponent;
type ElementType = string | FunctionComponent<Record<any, any>>;
```

**References:** [FunctionComponent](#functioncomponent)
Expand Down Expand Up @@ -877,7 +873,9 @@ Note that this includes QRLs.
The `key`, `flags` and `dev` parameters are for internal use.

```typescript
export type FunctionComponent<P extends Record<any, any> = Record<any, any>> = {
export type FunctionComponent<
P extends Record<any, any> = Record<any, unknown>,
> = {
renderFn(
props: P,
key: string | null,
Expand Down Expand Up @@ -1284,7 +1282,7 @@ jsxDEV: <T extends string | FunctionComponent>(
## JSXNode

```typescript
export interface JSXNode<T = string | FunctionComponent>
export interface JSXNode<T extends string | FunctionComponent | unknown = unknown>
```

| Property | Modifiers | Type | Description |
Expand Down Expand Up @@ -1645,12 +1643,10 @@ export interface OlHTMLAttributes<T extends Element> extends Attrs<'ol', T>
## OnRenderFn

```typescript
export type OnRenderFn<PROPS extends Record<any, any>> = (
props: PROPS,
) => JSXNode | null;
export type OnRenderFn<PROPS> = (props: PROPS) => JSXOutput;
```

**References:** [JSXNode](#jsxnode)
**References:** [JSXOutput](#jsxoutput)

[Edit this section](https://github.com/BuilderIO/qwik/tree/main/packages/qwik/src/core/component/component.public.ts)

Expand Down Expand Up @@ -1813,11 +1809,17 @@ export type PropsOf<COMP> = COMP extends string
: NonNullable<COMP> extends never
? never
: COMP extends FunctionComponent<infer PROPS>
? NonNullable<PROPS>
: Record<string, unknown>;
? PROPS extends Record<any, infer V>
? IsAny<V> extends true
? never
: ObjectProps<PROPS>
: COMP extends Component<infer OrigProps>
? ObjectProps<OrigProps>
: PROPS
: never;
```

**References:** [QwikIntrinsicElements](#qwikintrinsicelements), [FunctionComponent](#functioncomponent)
**References:** [QwikIntrinsicElements](#qwikintrinsicelements), [FunctionComponent](#functioncomponent), [Component](#component)

```tsx
const Desc = component$(
Expand All @@ -1844,11 +1846,11 @@ const TitleBox = component$(
Extends the defined component PROPS, adding the default ones (children and q:slot) and allowing plain functions to QRL arguments.

```typescript
export type PublicProps<PROPS extends Record<any, any>> = Omit<
PROPS,
`${string}$`
> &
_Only$<PROPS> &
export type PublicProps<PROPS> = (PROPS extends Record<any, any>
? Omit<PROPS, `${string}$`> & _Only$<PROPS>
: unknown extends PROPS
? {}
: PROPS) &
ComponentBaseProps &
ComponentChildren<PROPS>;
```
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -383,7 +383,7 @@ When you want to output a different type of element depending on props, you can

```tsx
export const Poly = component$(
<C extends string | FunctionComponent>({
<C extends string | FunctionComponent = string | FunctionComponent>({
as: Cmp = 'div' as C,
...props
}: { as?: C } & PropsOf<string extends C ? 'div' : C>) => {
Expand Down
7 changes: 2 additions & 5 deletions packages/eslint-plugin-qwik/src/validLexicalScope.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,17 +163,14 @@ export const validLexicalScope = createRule({
relevantScopes.set(scope, name);
} else if (firstArg.expression.type === 'Identifier') {
const tsNode = esTreeNodeToTSNodeMap.get(firstArg.expression);
const type = typeChecker.getTypeAtLocation(tsNode);
const type = typeChecker.getTypeAtLocation(tsNode).getNonNullableType();

if (!isTypeQRL(type)) {
if (type.isUnionOrIntersection()) {
if (
!type.types.every((t) => {
if (t.symbol) {
return t.symbol.name === 'PropFnInterface';
}
if (t.flags & (ts.TypeFlags.Undefined | ts.TypeFlags.Null)) {
return true;
return t.symbol.name === 'Component' || t.symbol.name === 'PropFnInterface';
}
return false;
})
Expand Down
2 changes: 1 addition & 1 deletion packages/qwik-city/runtime/src/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -404,7 +404,7 @@ export type RouteNavigate = QRL<(path?: string, options?: {
} | boolean) => Promise<void>>;

// @public (undocumented)
export const RouterOutlet: Component<Record<any, any>>;
export const RouterOutlet: Component<unknown>;

// @public (undocumented)
export const server$: <T extends ServerFunction>(first: T) => ServerQRL<T>;
Expand Down
Loading
Loading