Skip to content

Commit

Permalink
Typescript: Context.Provider value should not be assignable to undefi…
Browse files Browse the repository at this point in the history
…ned in the default case
  • Loading branch information
David Emanuel Luksic committed Nov 19, 2023
1 parent e7ea929 commit af13719
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 19 deletions.
12 changes: 6 additions & 6 deletions packages/solid/src/reactive/signal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1130,10 +1130,10 @@ export function registerGraph(value: SourceMapValue): void {
export type ContextProviderComponent<T> = FlowComponent<{ value: T }>;

// Context API
export interface Context<T> {
export interface Context<T, D = T> {
id: symbol;
Provider: ContextProviderComponent<T>;
defaultValue: T;
defaultValue: D;
}

/**
Expand All @@ -1158,7 +1158,7 @@ export interface Context<T> {
export function createContext<T>(
defaultValue?: undefined,
options?: EffectOptions
): Context<T | undefined>;
): Context<T, undefined>;
export function createContext<T>(defaultValue: T, options?: EffectOptions): Context<T>;
export function createContext<T>(
defaultValue?: T,
Expand All @@ -1176,7 +1176,7 @@ export function createContext<T>(
*
* @description https://www.solidjs.com/docs/latest/api#usecontext
*/
export function useContext<T>(context: Context<T>): T {
export function useContext<T, D>(context: Context<T, D>): D extends undefined ? T | undefined : T {
return Owner && Owner.context && Owner.context[context.id] !== undefined
? Owner.context[context.id]
: context.defaultValue;
Expand Down Expand Up @@ -1215,7 +1215,7 @@ export type SuspenseContextType = {
resolved?: boolean;
};

type SuspenseContext = Context<SuspenseContextType | undefined> & {
type SuspenseContext = Context<SuspenseContextType, undefined> & {
active?(): boolean;
increment?(): void;
decrement?(): void;
Expand All @@ -1224,7 +1224,7 @@ type SuspenseContext = Context<SuspenseContextType | undefined> & {
let SuspenseContext: SuspenseContext;

export function getSuspenseContext() {
return SuspenseContext || (SuspenseContext = createContext<SuspenseContextType | undefined>());
return SuspenseContext || (SuspenseContext = createContext<SuspenseContextType>());
}

// Interop
Expand Down
49 changes: 36 additions & 13 deletions packages/solid/web/test/context.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,14 @@ describe("Testing Context", () => {
</Show>
);
};
const div = document.createElement("div");

it("should create context properly", () => {
expect(ThemeContext.id).toBeDefined();
expect(ThemeContext.defaultValue).toBe("light");
});

it("should work with single provider child", () => {
const div = document.createElement("div");
render(
() => (
<ThemeContext.Provider value="dark">
Expand All @@ -35,11 +36,11 @@ describe("Testing Context", () => {
),
div
);
expect((div.firstChild as HTMLDivElement).innerHTML).toBe("dark");
div.innerHTML = "";
expect(div.children[0].innerHTML).toBe("dark");
});

it("should work with single conditional provider child", () => {
const div = document.createElement("div");
render(
() => (
<ThemeContext.Provider value="dark">
Expand All @@ -48,11 +49,11 @@ describe("Testing Context", () => {
),
div
);
expect((div.firstChild as HTMLDivElement).innerHTML).toBe("dark");
div.innerHTML = "";
expect(div.children[0].innerHTML).toBe("dark");
});

it("should work with multi provider child", () => {
const div = document.createElement("div");
render(
() => (
<ThemeContext.Provider value="dark">
Expand All @@ -62,11 +63,11 @@ describe("Testing Context", () => {
),
div
);
expect((div.firstChild!.nextSibling! as HTMLDivElement).innerHTML).toBe("dark");
div.innerHTML = "";
expect(div.children[1].innerHTML).toBe("dark");
});

it("should work with multi conditional provider child", () => {
const div = document.createElement("div");
render(
() => (
<ThemeContext.Provider value="dark">
Expand All @@ -76,11 +77,11 @@ describe("Testing Context", () => {
),
div
);
expect((div.firstChild!.nextSibling! as HTMLDivElement).innerHTML).toBe("dark");
div.innerHTML = "";
expect(div.children[1].innerHTML).toBe("dark");
});

it("should work with dynamic multi provider child", () => {
const div = document.createElement("div");
const child = () => <Component />;
render(
() => (
Expand All @@ -91,11 +92,11 @@ describe("Testing Context", () => {
),
div
);
expect((div.firstChild!.nextSibling! as HTMLDivElement).innerHTML).toBe("dark");
div.innerHTML = "";
expect(div.children[1].innerHTML).toBe("dark");
});

it("should work with dynamic multi conditional provider child", () => {
const div = document.createElement("div");
const child = () => <CondComponent />;
render(
() => (
Expand All @@ -106,7 +107,29 @@ describe("Testing Context", () => {
),
div
);
expect((div.firstChild!.nextSibling! as HTMLDivElement).innerHTML).toBe("dark");
div.innerHTML = "";
expect(div.children[1].innerHTML).toBe("dark");
});

const ThemeContextWithoutDefault = createContext<string>();
const ComponentWithoutDefault = () => {
const theme = useContext(ThemeContextWithoutDefault);
return <div>{theme ?? "no-default"}</div>;
};

it("should work with no default provided", () => {
const div = document.createElement("div");
render(
() => (
<>
<ComponentWithoutDefault />
<ThemeContextWithoutDefault.Provider value="dark">
<ComponentWithoutDefault />
</ThemeContextWithoutDefault.Provider>
</>
),
div
);
expect(div.children[0].innerHTML!).toBe("no-default");
expect(div.children[1].innerHTML!).toBe("dark");
});
});

0 comments on commit af13719

Please sign in to comment.