Skip to content

Commit

Permalink
some tweaks to types and add type tests for createElement
Browse files Browse the repository at this point in the history
  • Loading branch information
brainkim committed May 5, 2020
1 parent 183caa7 commit 22cac8a
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 36 deletions.
53 changes: 47 additions & 6 deletions src/__tests__/types.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,49 @@ import {
GeneratorComponent,
} from "..";

declare global {
module JSX {
interface IntrinsicElements {
myIntrinsic: {
message: string;
};
}
}
}
describe("types", () => {
type MyProps = {
message: string;
};

let elem: any;
test("createElement", () => {
const MyFunctionComponent: Component<MyProps> = function (this, props) {
const ctx: Context<MyProps> = this;
let message: string = props.message;
// @ts-expect-error
let unexpected = props.unexpected;

return <div></div>;
};

// @ts-expect-error
elem = createElement(MyFunctionComponent, {poop: 1});

// @ts-expect-error
elem = createElement(MyFunctionComponent, {message: 1});

elem = createElement(MyFunctionComponent, {message: "hello"});

// @ts-expect-error
elem = createElement("myIntrinsic", {poop: 1});

test("Not Components", () => {
// @ts-expect-error
elem = createElement("myIntrinsic", {message: 1});

elem = createElement("myIntrinsic", {message: "hello"});
});

test("not components", () => {
// @ts-expect-error
const MyString: Component = "Hello";

Expand All @@ -39,7 +75,7 @@ describe("types", () => {
const NotComponent: Component = {};
});

test("Components", () => {
test("Component", () => {
const MyFunctionComponent: Component<MyProps> = function (this, props) {
const ctx: Context<MyProps> = this;
let message: string = props.message;
Expand Down Expand Up @@ -112,7 +148,7 @@ describe("types", () => {
elem = <MyAsyncGeneratorComponent message={"message"} />;
});

test("Function Components", () => {
test("FunctionComponent", () => {
const MyFunctionComponent: FunctionComponent<MyProps> = function (
this,
props,
Expand Down Expand Up @@ -159,7 +195,7 @@ describe("types", () => {
};
});

test("Generator Components", () => {
test("GeneratorComponent", () => {
const MyGeneratorComponent: GeneratorComponent<MyProps> = function* (
this,
initialProps,
Expand Down Expand Up @@ -205,14 +241,15 @@ describe("types", () => {
elem = <MyAsyncGeneratorComponent message={"message"} />;

// TODO: add ts-expect-error at some point in the future, I guess?
// This will not pass because the function is infered as any, and any matches Iterator<any, any, any>.
// This will not pass because the function is inferred as any, and any matches Iterator<any, any, any>.
// Hopefully a later typescript version fixes this :(
const MyFunctionComponent: GeneratorComponent<MyProps> = function (
this,
props,
) {
return <div></div>;
};

// @ts-expect-error
const MyAsyncFunctionComponent: GeneratorComponent<MyProps> = async function (
this,
Expand All @@ -222,14 +259,15 @@ describe("types", () => {
};
});

test("Loose Typings", () => {
test("loose typings", () => {
function MyFunctionComponent(props: MyProps) {
let message: string = props.message;
// @ts-expect-error
let unexpected = props.unexpected;

return <div></div>;
}

// @ts-expect-error
elem = <MyFunctionComponent />;
elem = <MyFunctionComponent message={"message"} />;
Expand All @@ -241,6 +279,7 @@ describe("types", () => {

return <div></div>;
}

// @ts-expect-error
elem = <MyAsyncFunctionComponent />;
elem = <MyAsyncFunctionComponent message={"message"} />;
Expand All @@ -259,6 +298,7 @@ describe("types", () => {

return <div></div>;
}

// @ts-expect-error
elem = <MyGeneratorComponent />;
elem = <MyGeneratorComponent message={"message"} />;
Expand All @@ -277,6 +317,7 @@ describe("types", () => {

return <div></div>;
}

// @ts-expect-error
elem = <MyAsyncGeneratorComponent />;
elem = <MyAsyncGeneratorComponent message={"message"} />;
Expand Down
9 changes: 5 additions & 4 deletions src/dom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,11 @@ declare module "./index" {
}

// TODO: create an allowlist/blocklist of props
type AnyProps = Props & {
[name: string]: any;
};
function updateProps(el: Element, props: AnyProps, newProps: AnyProps): void {
function updateProps(
el: Element,
props: Record<string, any>,
newProps: Record<string, any>,
): void {
for (const name in {...props, ...newProps}) {
const value = props[name];
const newValue = newProps[name];
Expand Down
49 changes: 23 additions & 26 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,11 @@ function isIteratorOrAsyncIterator(

export type Tag<TProps = any> = Component<TProps> | string | symbol;

// prettier-ignore
type TagProps<TTag extends Tag> =
TTag extends Component<infer TProps> ? TProps
: TTag extends string ? JSX.IntrinsicElements[TTag]
: any;
type TagProps<TTag extends Tag> = TTag extends Component<infer TProps>
? TProps
: TTag extends string
? JSX.IntrinsicElements[TTag]
: unknown;

export type Key = unknown;

Expand Down Expand Up @@ -121,7 +121,7 @@ export function createElement<TTag extends Tag>(
props?: TagProps<TTag> | null,
...children: Array<Children>
): Element<TTag>;
export function createElement<TTag extends Tag, TProps extends Props>(
export function createElement<TTag extends Tag>(
tag: TTag,
props?: TagProps<TTag> | null,
): Element<TTag> {
Expand Down Expand Up @@ -196,10 +196,9 @@ class LeafNode<T> implements NodeBase<T> {
value: string | undefined = undefined;
}

abstract class ParentNode<T, TProps extends Props = Props>
implements NodeBase<T> {
abstract class ParentNode<T> implements NodeBase<T> {
readonly internal = true;
abstract readonly tag: Tag<TProps>;
abstract readonly tag: Tag;
readonly key: Key = undefined;
nextSibling: Node<T> | undefined = undefined;
previousSibling: Node<T> | undefined = undefined;
Expand All @@ -216,9 +215,9 @@ abstract class ParentNode<T, TProps extends Props = Props>
private onNextResult:
| ((result?: Promise<undefined>) => unknown)
| undefined = undefined;
protected props: TProps | undefined = undefined;
protected props: any = undefined;
value: Array<T | string> | T | string | undefined = undefined;
ctx: Context<TProps> | undefined = undefined;
ctx: Context | undefined = undefined;
protected updating = false;
protected iterating = false;
protected finished = false;
Expand Down Expand Up @@ -512,7 +511,7 @@ abstract class ParentNode<T, TProps extends Props = Props>
}
}

update(props: TProps): MaybePromise<undefined> {
update(props: any): MaybePromise<undefined> {
this.props = props;
this.updating = true;
return this.refresh();
Expand All @@ -539,7 +538,7 @@ abstract class ParentNode<T, TProps extends Props = Props>
}
}

class FragmentNode<T> extends ParentNode<T, any> {
class FragmentNode<T> extends ParentNode<T> {
readonly tag: Fragment = Fragment;
readonly key: Key;
readonly parent: ParentNode<T>;
Expand Down Expand Up @@ -574,7 +573,7 @@ class FragmentNode<T> extends ParentNode<T, any> {
}
}

class HostNode<T> extends ParentNode<T, any> {
class HostNode<T> extends ParentNode<T> {
readonly tag: string | symbol;
readonly key: Key;
readonly parent: ParentNode<T> | undefined;
Expand Down Expand Up @@ -681,12 +680,12 @@ type AsyncGen = typeof AsyncGen;

type ComponentType = SyncFn | AsyncFn | SyncGen | AsyncGen;

class ComponentNode<T, TProps extends Props> extends ParentNode<T, TProps> {
class ComponentNode<T, TProps> extends ParentNode<T> {
readonly tag: Component<TProps>;
readonly key: Key;
readonly parent: ParentNode<T, any>;
readonly parent: ParentNode<T>;
readonly renderer: Renderer<T>;
readonly ctx: Context<TProps>;
readonly ctx: Context;
private stepping = false;
private available = false;
private iterator: ChildIterator | undefined = undefined;
Expand All @@ -700,9 +699,9 @@ class ComponentNode<T, TProps extends Props> extends ParentNode<T, TProps> {
private provisions: Map<unknown, any> | undefined = undefined;
private publish: ((props: TProps) => unknown) | undefined = undefined;
constructor(
parent: ParentNode<T, any>,
parent: ParentNode<T>,
renderer: Renderer<T>,
tag: Component<TProps>,
tag: Component,
key: Key,
) {
super();
Expand Down Expand Up @@ -986,7 +985,7 @@ class ComponentNode<T, TProps extends Props> extends ParentNode<T, TProps> {
}

function createNode<T>(
parent: ParentNode<T, any>,
parent: ParentNode<T>,
renderer: Renderer<T>,
child: NormalizedChild,
): Node<T> {
Expand All @@ -996,10 +995,8 @@ function createNode<T>(
return new FragmentNode(parent, renderer, child.key);
} else if (typeof child.tag === "function") {
return new ComponentNode(parent, renderer, child.tag, child.key);
} else if (typeof child.tag === "string" || typeof child.tag === "symbol") {
return new HostNode(parent, renderer, child.tag, child.key);
} else {
throw new Error("Unsupported child/tag type");
return new HostNode(parent, renderer, child.tag, child.key);
}
}

Expand All @@ -1018,7 +1015,7 @@ export interface ProvisionMap {}

const componentNodes = new WeakMap<Context<any>, ComponentNode<any, any>>();
export class Context<TProps = any> extends CrankEventTarget {
constructor(host: ComponentNode<any, any>, parent?: Context<Props>) {
constructor(host: ComponentNode<any, TProps>, parent?: Context<TProps>) {
super(parent);
componentNodes.set(this, host);
}
Expand All @@ -1037,11 +1034,11 @@ export class Context<TProps = any> extends CrankEventTarget {
}
/* eslint-enable no-dupe-class-members */

[Symbol.iterator](): Generator<TProps & Props> {
[Symbol.iterator](): Generator<TProps> {
return componentNodes.get(this)![Symbol.iterator]();
}

[Symbol.asyncIterator](): AsyncGenerator<TProps & Props> {
[Symbol.asyncIterator](): AsyncGenerator<TProps> {
return componentNodes.get(this)![Symbol.asyncIterator]();
}

Expand Down

0 comments on commit 22cac8a

Please sign in to comment.