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

Revised Component types (children, ref) #877

Merged
merged 14 commits into from
May 3, 2022
9 changes: 5 additions & 4 deletions packages/solid/src/reactive/signal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import { requestCallback, Task } from "./scheduler";
import { sharedConfig } from "../render/hydration";
import type { JSX } from "../jsx";
import type { FlowComponent, FlowProps } from "../render";

export const equalFn = <T>(a: T, b: T) => a === b;
export const $PROXY = Symbol("solid-proxy");
Expand Down Expand Up @@ -1049,7 +1050,7 @@ export function serializeGraph(owner?: Owner | null): GraphRecord {
};
}

export type ContextProviderComponent<T> = (props: { value: T; children: any }) => any;
export type ContextProviderComponent<T> = FlowComponent<{ value: T; }>;

// Context API
export interface Context<T> {
Expand All @@ -1063,7 +1064,7 @@ export interface Context<T> {
* ```typescript
* interface Context<T> {
* id: symbol;
* Provider: (props: { value: T; children: any }) => any;
* Provider: ParentComponent<{ value: T }>;
Copy link
Contributor

Choose a reason for hiding this comment

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

You probably forgot to change this to FlowComponent

* defaultValue: T;
* }
* export function createContext<T>(defaultValue?: T): Context<T | undefined>;
Expand Down Expand Up @@ -1601,7 +1602,7 @@ function resolveChildren(children: JSX.Element): ResolvedChildren {
}

function createProvider(id: symbol) {
return function provider(props: { value: unknown; children: JSX.Element }) {
return function provider(props: FlowProps<{ value: unknown; }>) {
let res;
createComputed(
() =>
Expand All @@ -1610,7 +1611,7 @@ function createProvider(id: symbol) {
return children(() => props.children);
}))
);
return res as JSX.Element;
return res;
};
}

Expand Down
44 changes: 41 additions & 3 deletions packages/solid/src/render/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,38 @@ export function enableHydration() {
hydrationEnabled = true;
}

export type PropsWithChildren<P = {}> = P & { children?: JSX.Element };
export type Component<P = {}> = (props: PropsWithChildren<P>) => JSX.Element;
/**
* A general `Component` has no implicit `children` prop. If desired, you can
* specify one as in `Component<{name: String, children: JSX.Element>}`.
*/
export type Component<P = {}> = (props: P) => JSX.Element;

/**
* `VoidComponent` forbids the `children` prop.
* Use this to prevent accidentally passing `children` to components that
* would silently throw them away.
*/
export type VoidProps<P = {}> = P & { children?: never };
export type VoidComponent<P = {}> = Component<VoidProps<P>>;

/**
* `ParentComponent` allows an optional the `children` prop with the usual
* type in JSX, `JSX.Element` (which allows elements, arrays, functions, etc.).
* Use this for components that you want to accept children.
*/
export type ParentProps<P = {}> = P & { children?: JSX.Element };
export type ParentComponent<P = {}> = Component<ParentProps<P>>;

/**
* `FlowComponent` requires a `children` prop with the specified type.
* Use this for components where you need specific types of children.
*/
export type FlowProps<P = {}, C = JSX.Element> = P & { children: C };
export type FlowComponent<P = {}, C = JSX.Element> = Component<FlowProps<P, C>>;

/** @deprecated */
export type PropsWithChildren<P = {}> = ParentProps<P>;

/**
* Takes the props of the passed component and returns its type
*
Expand All @@ -30,7 +60,15 @@ export type ComponentProps<T extends keyof JSX.IntrinsicElements | Component<any
: T extends keyof JSX.IntrinsicElements
? JSX.IntrinsicElements[T]
: {};
export function createComponent<T>(Comp: (props: T) => JSX.Element, props: T): JSX.Element {

/**
* Type of `props.ref`, for use in `Component` or `props` typing.
*
* @example Component<{ref: Ref<Element>}>
*/
export type Ref<T> = T | ((val: T) => void);

export function createComponent<T>(Comp: Component<T>, props: T): JSX.Element {
if (hydrationEnabled) {
if (sharedConfig.context) {
const c = sharedConfig.context;
Expand Down
2 changes: 1 addition & 1 deletion packages/solid/src/server/reactive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ export function children(fn: () => any) {
return createMemo(() => resolveChildren(fn()));
}

export function runWithOwner(o: Owner, fn: () => any) {
export function runWithOwner<T>(o: Owner, fn: () => T): T {
const prev = Owner;
Owner = o;
try {
Expand Down
28 changes: 22 additions & 6 deletions packages/solid/src/server/rendering.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,20 @@
import { Owner, createContext, createMemo, useContext, runWithOwner, onError } from "./reactive";
import type { JSX } from "../jsx";

type PropsWithChildren<P> = P & { children?: JSX.Element };
export type Component<P = {}> = (props: PropsWithChildren<P>) => JSX.Element;
//export type Component<P = {}> = (props: P) => JSX.Element;
export interface Component<P = {}> {
(props: P): JSX.Element
}
export type VoidProps<P = {}> = P & { children?: never };
export type VoidComponent<P = {}> = Component<VoidProps<P>>;
export type ParentProps<P = {}> = P & { children?: JSX.Element };
export type ParentComponent<P = {}> = Component<ParentProps<P>>;
export type FlowProps<P = {}, C = JSX.Element> = P & { children: C };
export type FlowComponent<P = {}, C = JSX.Element> = Component<FlowProps<P, C>>;
export type Ref<T> = T | ((val: T) => void);
export type ComponentProps<T extends keyof JSX.IntrinsicElements | Component> =
T extends Component<infer P> ? P :
T extends keyof JSX.IntrinsicElements ? JSX.IntrinsicElements[T] : {};

type PossiblyWrapped<T> = {
[P in keyof T]: T[P] | (() => T[P]);
Expand Down Expand Up @@ -354,12 +366,16 @@ export function createResource<T, U>(

export function refetchResources(info?: unknown) { }

export function lazy(fn: () => Promise<{ default: any }>): (props: any) => string {
let resolved: (props: any) => any;
export function lazy<T extends Component<any>>(
fn: () => Promise<{ default: T }>
): T & { preload: () => Promise<{ default: T }> } {
let resolved: T;
const p = fn();
const contexts = new Set<SuspenseContextType>();
p.then(mod => (resolved = mod.default));
const wrap = (props: any) => {
const wrap: Component<ComponentProps<T>> &
{ preload?: () => Promise<{ default: T }> }
= (props) => {
const id = sharedConfig.context!.id.slice(0, -1);
if (resolved) return resolved(props);
const ctx = useContext(SuspenseContext);
Expand All @@ -376,7 +392,7 @@ export function lazy(fn: () => Promise<{ default: any }>): (props: any) => strin
return "";
};
wrap.preload = () => p;
return wrap;
return wrap as T & { preload: () => Promise<{ default: T }> };
}

function suspenseComplete(c: SuspenseContextType) {
Expand Down