Skip to content

unable to narrow types with ElementAttributes with noUncheckedIndexedAccess enabled in tsconfig #21

@bigmistqke

Description

@bigmistqke

hey joe 👋

i have been playing around with lume/elements to make custom-element export for solid-shiki-textarea.

Issue

I noticed that the attributes of the custom-element were always string, while I defined the type with a more narrow type:

class ShikiTextareaElement extends Element {
  @stringAttribute lang = 'tsx' as BundledLanguage
  ...
}

I extended the namespace with ElementAttributes<ShikiTextareaElement, ...>:

declare module 'solid-js/jsx-runtime' {
  namespace JSX {
    interface IntrinsicElements {
      'shiki-textarea': ElementAttributes<ShikiTextareaElement, 'cdn' | 'lang' | 'theme' | 'value'>
    }
  }
}

Own exploration

I had a look inside ElementAttributes:

export type ElementAttributes<
	ElementType,
	SelectedProperties extends keyof ElementType,
	AdditionalProperties extends object = {},
> = WithStringValues<DashCasedProps<Partial<Pick<ElementType, SelectedProperties>>>> &
	AdditionalProperties &
	Omit<JSX.HTMLAttributes<ElementType>, SelectedProperties | keyof AdditionalProperties>

type WithStringValues<Type extends object> = {
	[Property in keyof Type]: Type[Property] extends string ? Type[Property] : Type[Property] | string
}

And found that the culprit was Type[Property] extends string.

Type[Property] is in my setup typed as BundledLanguage | undefined which causes it to not fullfill the conditional. Type[Property] | string causes the type to be widened to string.

The fact that Type[Property] is typed as BundledLanguage | undefined and not BundledLanguage is due to my tsconfig setting: "noUncheckedIndexedAccess": true.

Patch

I was able to patch it to create the behavior that I wanted in the following way:

type WithStringValues<Type extends object> = {
	[Property in keyof Type]: NonNullable<Type[Property]> extends string ? Type[Property] : Type[Property] | string
}

where I wrapped Type[Property]> in a NonNullable to exclude undefined from its type.

I am not sure if this is the correct solution, that's why I preferred to write it up as an issue instead of a PR.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions