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 { setHydrateContext, 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 @@ -1047,7 +1048,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 @@ -1061,7 +1062,7 @@ export interface Context<T> {
* ```typescript
* interface Context<T> {
* id: symbol;
* Provider: (props: { value: T; children: any }) => any;
* Provider: FlowComponent<{ value: T }>;
* defaultValue: T;
* }
* export function createContext<T>(defaultValue?: T): Context<T | undefined>;
Expand Down Expand Up @@ -1604,7 +1605,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 @@ -1613,7 +1614,7 @@ function createProvider(id: symbol) {
return children(() => props.children);
}))
);
return res as JSX.Element;
return res;
};
}

Expand Down
62 changes: 59 additions & 3 deletions packages/solid/src/render/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,56 @@ 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;

/**
* Extend props to forbid 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 };
/**
* `VoidComponent` forbids the `children` prop.
* Use this to prevent accidentally passing `children` to components that
* would silently throw them away.
*/
export type VoidComponent<P = {}> = Component<VoidProps<P>>;

/**
* Extend props to allow an optional `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 };
/**
* `ParentComponent` allows an optional `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 ParentComponent<P = {}> = Component<ParentProps<P>>;

/**
* Extend props to require a `children` prop with the specified type.
* Use this for components where you need a specific child type,
* typically a function that receives specific argument types.
* Note that all JSX <Elements> are of the type `JSX.Element`.
*/
export type FlowProps<P = {}, C = JSX.Element> = P & { children: C };
/**
* `FlowComponent` requires a `children` prop with the specified type.
* Use this for components where you need a specific child type,
* typically a function that receives specific argument types.
* Note that all JSX <Elements> are of the type `JSX.Element`.
*/
export type FlowComponent<P = {}, C = JSX.Element> = Component<FlowProps<P, C>>;

/** @deprecated: use `ParentProps` instead */
export type PropsWithChildren<P = {}> = ParentProps<P>;

/**
* Takes the props of the passed component and returns its type
*
Expand All @@ -30,7 +78,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 (props == null || typeof props !== "object") props = {} as T;
if (hydrationEnabled) {
if (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 @@ -162,7 +162,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
25 changes: 19 additions & 6 deletions packages/solid/src/server/rendering.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,17 @@ import {
} 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 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 @@ -385,12 +394,16 @@ export function createResource<T, S>(
] as ResourceReturn<T>);
}

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 @@ -407,7 +420,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