Skip to content

Fix problem with transitive generics in Signatures #613

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

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
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
3 changes: 2 additions & 1 deletion packages/template/-private/signature.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ export type ComponentSignatureBlocks<S> = S extends { Blocks: infer Blocks }

/** Given a component signature `S`, get back the `Element` type. */
export type ComponentSignatureElement<S> = S extends { Element: infer Element }
? NonNullable<Element> extends never
? NonNullable<Element> extends never
// ? Element extends null
Comment on lines +48 to +49
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

this is a temporary revert. That is to write failing tests, which when switching back to the fix should be resolved.

? unknown
: Element
: unknown;
Expand Down
54 changes: 54 additions & 0 deletions packages/template/__tests__/signature.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
ComponentSignatureBlocks,
ComponentSignatureElement,
} from '../-private/signature';
import { ComponentLike } from '../';

type LegacyArgs = {
foo: number;
Expand Down Expand Up @@ -153,3 +154,56 @@ interface FullLongSig {
expectTypeOf<ComponentSignatureArgs<FullLongSig>>().toEqualTypeOf<FullLongSig['Args']>();
expectTypeOf<ComponentSignatureBlocks<FullLongSig>>().toEqualTypeOf<FullLongSig['Blocks']>();
expectTypeOf<ComponentSignatureElement<FullLongSig>>().toEqualTypeOf<FullLongSig['Element']>();

// types to simulate the `(element)` helper
// Issue: https://github.com/typed-ember/glint/issues/610
type ElementFromTagName<T extends string> = T extends keyof HTMLElementTagNameMap
? HTMLElementTagNameMap[T]
: Element;
type ElementHelperPositional<T extends string> = [name: T];
type ElementHelperReturn<T extends string> = ComponentLike<{
Element: ElementFromTagName<T>;
Blocks: { default: [] };
}>;

interface ElementSignature<T extends string> {
Args: {
Positional: ElementHelperPositional<T>;
};
Return: ElementHelperReturn<T> | undefined;
}

// signature for a component receiving an `(element)`
interface ElementReceiverSignature<T extends string> {
Element: ElementFromTagName<T>;
Args: {
element: ElementSignature<T>['Return'];
};
Blocks: {
default: [];
};
}

expectTypeOf<ComponentSignatureArgs<ElementReceiverSignature<'div'>>>().toEqualTypeOf<{
Named: {
element: ElementSignature<'div'>['Return'];
};
Positional: []
}>();

expectTypeOf<ComponentSignatureArgs<ElementReceiverSignature<null>>>().toEqualTypeOf<{
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Actually, this line is the problematic part here, I'd say. For what I think needs to be tested, is how the components are invoked, rather than the static definition?

That is, in invocation, this should be null. But this is obviously a failure:

Type 'null' does not satisfy the constraint 'string'.

To my knowledge, to properly test this, is to provide broken types. How to do that?

Named: {
element: {
Args: {
Positional: ElementHelperPositional<never>;
};
Return: ComponentLike<{
Element: Element;
Blocks: { default: [] };
}> | undefined;
} | undefined;
};
Positional: []
}>();
expectTypeOf<ComponentSignatureElement<ElementReceiverSignature<'div'>>>().toEqualTypeOf<HTMLDivElement>();

Loading