Skip to content

Commit ae49c53

Browse files
committed
fix(types): avoid as any
dang ol' nerd sniped
1 parent 6d0eeb8 commit ae49c53

File tree

3 files changed

+41
-21
lines changed

3 files changed

+41
-21
lines changed

packages/@lwc/shared/src/aria.ts

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -74,21 +74,36 @@ const AriaPropertyNames = [
7474
'role',
7575
] as const;
7676

77+
type AriaProperty = (typeof AriaPropertyNames)[number];
78+
7779
export type AccessibleElementProperties = {
78-
[prop in (typeof AriaPropertyNames)[number]]: string | null;
80+
[Prop in AriaProperty]: string | null;
81+
};
82+
83+
type AriaPropToAttrMap = {
84+
[Prop in AriaProperty]: Prop extends `aria${infer S}` ? `aria-${Lowercase<S>}` : Prop;
85+
};
86+
87+
type AriaAttribute = AriaPropToAttrMap[AriaProperty];
88+
89+
type AriaAttrToPropMap = {
90+
[Prop in AriaProperty as AriaPropToAttrMap[Prop]]: Prop;
7991
};
8092

8193
const { AriaAttrNameToPropNameMap, AriaPropNameToAttrNameMap } = /*@__PURE__*/ (() => {
82-
const AriaAttrNameToPropNameMap: Record<string, string> = create(null);
83-
const AriaPropNameToAttrNameMap: Record<string, string> = create(null);
94+
const AriaAttrNameToPropNameMap: AriaAttrToPropMap = create(null);
95+
const AriaPropNameToAttrNameMap: AriaPropToAttrMap = create(null);
8496

8597
// Synthetic creation of all AOM property descriptors for Custom Elements
8698
forEach.call(AriaPropertyNames, (propName) => {
8799
const attrName = StringToLowerCase.call(
88100
StringReplace.call(propName, /^aria/, () => 'aria-')
89-
);
90-
AriaAttrNameToPropNameMap[attrName] = propName;
91-
AriaPropNameToAttrNameMap[propName] = attrName;
101+
) as AriaAttribute;
102+
// These type assertions are because the map types are a 1:1 mapping of ariaX to aria-x.
103+
// TypeScript knows we have one of ariaX | ariaY and one of aria-x | aria-y, and tries to
104+
// prevent us from doing ariaX: aria-y, but we that it's safe.
105+
(AriaAttrNameToPropNameMap[attrName] as AriaProperty) = propName;
106+
(AriaPropNameToAttrNameMap[propName] as AriaAttribute) = attrName;
92107
});
93108

94109
return { AriaAttrNameToPropNameMap, AriaPropNameToAttrNameMap };

packages/@lwc/shared/src/html-attributes.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,8 @@ const CACHED_PROPERTY_ATTRIBUTE_MAPPING = /*@__PURE__@*/ new Map<string, string>
127127
* @param propName
128128
*/
129129
export function htmlPropertyToAttribute(propName: string): string {
130-
const ariaAttributeName = AriaPropNameToAttrNameMap[propName];
130+
const ariaAttributeName =
131+
AriaPropNameToAttrNameMap[propName as keyof typeof AriaPropNameToAttrNameMap];
131132
if (!isUndefined(ariaAttributeName)) {
132133
return ariaAttributeName;
133134
}

packages/@lwc/ssr-runtime/src/reflection.ts

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -21,18 +21,21 @@ import type { LightningElement } from './lightning-element';
2121
* Map of global attribute or ARIA attribute to the corresponding property name.
2222
* Not all global attributes are included, just those from `HTMLElementTheGoodParts`.
2323
*/
24-
const attrsToProps = assign(create(null), {
25-
accesskey: 'accessKey',
26-
dir: 'dir',
27-
draggable: 'draggable',
28-
hidden: 'hidden',
29-
id: 'id',
30-
lang: 'lang',
31-
spellcheck: 'spellcheck',
32-
tabindex: 'tabIndex',
33-
title: 'title',
34-
...AriaAttrNameToPropNameMap,
35-
});
24+
const attrsToProps = assign(
25+
create(null) as object,
26+
{
27+
accesskey: 'accessKey',
28+
dir: 'dir',
29+
draggable: 'draggable',
30+
hidden: 'hidden',
31+
id: 'id',
32+
lang: 'lang',
33+
spellcheck: 'spellcheck',
34+
tabindex: 'tabIndex',
35+
title: 'title',
36+
...AriaAttrNameToPropNameMap,
37+
} as const
38+
);
3639

3740
/**
3841
* Descriptor for IDL attribute reflections that merely reflect the string, e.g. `title`.
@@ -127,9 +130,10 @@ export function reflectAttrToProp(
127130
const reflectedPropName = attrsToProps[attrName as keyof typeof attrsToProps];
128131
// If it is a reflected property and it was not overridden by the instance
129132
if (reflectedPropName && !hasOwnProperty.call(instance, reflectedPropName)) {
130-
const currentValue = (instance as any)[reflectedPropName];
133+
const currentValue = instance[reflectedPropName];
131134
if (currentValue !== attrValue) {
132-
(instance as any)[reflectedPropName] = attrValue;
135+
// Type assertion because we're providing a string, but some props accept boolean/number
136+
(instance[reflectedPropName] as string | null) = attrValue;
133137
}
134138
}
135139
}

0 commit comments

Comments
 (0)