Skip to content

Commit dbf56b1

Browse files
Add more precise typing for autocomplete HTML attribute (#1467)
Co-authored-by: saschanaz <[email protected]>
1 parent 2d110ad commit dbf56b1

File tree

5 files changed

+234
-5
lines changed

5 files changed

+234
-5
lines changed

baselines/dom.generated.d.ts

+15-4
Original file line numberDiff line numberDiff line change
@@ -10260,7 +10260,7 @@ interface HTMLFormElement extends HTMLElement {
1026010260
*
1026110261
* [MDN Reference](https://developer.mozilla.org/docs/Web/API/HTMLFormElement/autocomplete)
1026210262
*/
10263-
autocomplete: string;
10263+
autocomplete: AutoFillBase;
1026410264
/**
1026510265
* Retrieves a collection, in source order, of all controls in a given form.
1026610266
*
@@ -10928,7 +10928,7 @@ interface HTMLInputElement extends HTMLElement, PopoverInvokerElement {
1092810928
*
1092910929
* [MDN Reference](https://developer.mozilla.org/docs/Web/API/HTMLInputElement/autocomplete)
1093010930
*/
10931-
autocomplete: string;
10931+
autocomplete: AutoFill;
1093210932
capture: string;
1093310933
/** Sets or retrieves the state of the check box or radio button. */
1093410934
checked: boolean;
@@ -12309,7 +12309,7 @@ declare var HTMLScriptElement: {
1230912309
*/
1231012310
interface HTMLSelectElement extends HTMLElement {
1231112311
/** [MDN Reference](https://developer.mozilla.org/docs/Web/API/HTMLSelectElement/autocomplete) */
12312-
autocomplete: string;
12312+
autocomplete: AutoFill;
1231312313
/** [MDN Reference](https://developer.mozilla.org/docs/Web/API/HTMLSelectElement/disabled) */
1231412314
disabled: boolean;
1231512315
/**
@@ -13111,7 +13111,7 @@ declare var HTMLTemplateElement: {
1311113111
*/
1311213112
interface HTMLTextAreaElement extends HTMLElement {
1311313113
/** [MDN Reference](https://developer.mozilla.org/docs/Web/API/HTMLTextAreaElement/autocomplete) */
13114-
autocomplete: string;
13114+
autocomplete: AutoFill;
1311513115
/** Sets or retrieves the width of the object. */
1311613116
cols: number;
1311713117
/** Sets or retrieves the initial contents of the object. */
@@ -27926,6 +27926,9 @@ declare function addEventListener(type: string, listener: EventListenerOrEventLi
2792627926
declare function removeEventListener<K extends keyof WindowEventMap>(type: K, listener: (this: Window, ev: WindowEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
2792727927
declare function removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void;
2792827928
type AlgorithmIdentifier = Algorithm | string;
27929+
type AutoFill = AutoFillBase | `${OptionalPrefixToken<AutoFillSection>}${OptionalPrefixToken<AutoFillAddressKind>}${AutoFillField}${OptionalPostfixToken<AutoFillCredentialField>}`;
27930+
type AutoFillField = AutoFillNormalField | `${OptionalPrefixToken<AutoFillContactKind>}${AutoFillContactField}`;
27931+
type AutoFillSection = `section-${string}`;
2792927932
type BigInteger = Uint8Array;
2793027933
type BinaryData = ArrayBuffer | ArrayBufferView;
2793127934
type BlobPart = BufferSource | Blob | string;
@@ -27976,6 +27979,8 @@ type NamedCurve = string;
2797627979
type OffscreenRenderingContext = OffscreenCanvasRenderingContext2D | ImageBitmapRenderingContext | WebGLRenderingContext | WebGL2RenderingContext;
2797727980
type OnBeforeUnloadEventHandler = OnBeforeUnloadEventHandlerNonNull | null;
2797827981
type OnErrorEventHandler = OnErrorEventHandlerNonNull | null;
27982+
type OptionalPostfixToken<T extends string> = ` ${T}` | "";
27983+
type OptionalPrefixToken<T extends string> = `${T} ` | "";
2797927984
type PerformanceEntryList = PerformanceEntry[];
2798027985
type ReadableStreamController<T> = ReadableStreamDefaultController<T> | ReadableByteStreamController;
2798127986
type ReadableStreamReadResult<T> = ReadableStreamReadValueResult<T> | ReadableStreamReadDoneResult<T>;
@@ -28000,6 +28005,12 @@ type AudioContextLatencyCategory = "balanced" | "interactive" | "playback";
2800028005
type AudioContextState = "closed" | "running" | "suspended";
2800128006
type AuthenticatorAttachment = "cross-platform" | "platform";
2800228007
type AuthenticatorTransport = "ble" | "hybrid" | "internal" | "nfc" | "usb";
28008+
type AutoFillAddressKind = "billing" | "shipping";
28009+
type AutoFillBase = "" | "off" | "on";
28010+
type AutoFillContactField = "email" | "tel" | "tel-area-code" | "tel-country-code" | "tel-extension" | "tel-local" | "tel-local-prefix" | "tel-local-suffix" | "tel-national";
28011+
type AutoFillContactKind = "home" | "mobile" | "work";
28012+
type AutoFillCredentialField = "webauthn";
28013+
type AutoFillNormalField = "additional-name" | "address-level1" | "address-level2" | "address-level3" | "address-level4" | "address-line1" | "address-line2" | "address-line3" | "bday-day" | "bday-month" | "bday-year" | "cc-csc" | "cc-exp" | "cc-exp-month" | "cc-exp-year" | "cc-family-name" | "cc-given-name" | "cc-name" | "cc-number" | "cc-type" | "country" | "country-name" | "current-password" | "family-name" | "given-name" | "honorific-prefix" | "honorific-suffix" | "name" | "new-password" | "one-time-code" | "organization" | "postal-code" | "street-address" | "transaction-amount" | "transaction-currency" | "username";
2800328014
type AutoKeyword = "auto";
2800428015
type AutomationRate = "a-rate" | "k-rate";
2800528016
type AvcBitstreamFormat = "annexb" | "avc";

inputfiles/addedTypes.jsonc

+128
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,98 @@
352352
// https://developer.mozilla.org/en-US/docs/Web/API/WebXR_Device_API#browser_compatibility
353353
"xr-spatial-tracking"
354354
]
355+
},
356+
"AutoFillBase": {
357+
"name": "AutoFillBase",
358+
"value": [
359+
// Off
360+
"off",
361+
// Automatic
362+
"on",
363+
""
364+
]
365+
},
366+
"AutoFillAddressKind": {
367+
"name": "AutoFillAddressKind",
368+
"value": [
369+
"shipping",
370+
"billing"
371+
]
372+
},
373+
"AutoFillNormalField": {
374+
"name": "AutoFillNormalField",
375+
"value": [
376+
"name",
377+
"honorific-prefix",
378+
"given-name",
379+
"additional-name",
380+
"family-name",
381+
"honorific-suffix",
382+
383+
"username",
384+
"new-password",
385+
"current-password",
386+
// Supported in iOS Safari too even though WPT tests
387+
// for Safari currently fail as of 2023-06.
388+
"one-time-code",
389+
390+
"organization",
391+
"street-address",
392+
"address-line1",
393+
"address-line2",
394+
"address-line3",
395+
"address-level4",
396+
"address-level3",
397+
"address-level2",
398+
"address-level1",
399+
"country",
400+
"country-name",
401+
"postal-code",
402+
403+
"cc-name",
404+
"cc-given-name",
405+
"cc-family-name",
406+
"cc-number",
407+
"cc-exp",
408+
"cc-exp-month",
409+
"cc-exp-year",
410+
"cc-csc",
411+
"cc-type",
412+
"transaction-currency",
413+
"transaction-amount",
414+
415+
"bday-day",
416+
"bday-month",
417+
"bday-year"
418+
]
419+
},
420+
"AutoFillContactKind": {
421+
"name": "AutoFillContactKind",
422+
"value": [
423+
"home",
424+
"work",
425+
"mobile"
426+
]
427+
},
428+
"AutoFillContactField": {
429+
"name": "AutoFillContactField",
430+
"value": [
431+
"tel",
432+
"tel-country-code",
433+
"tel-national",
434+
"tel-area-code",
435+
"tel-local",
436+
"tel-local-prefix",
437+
"tel-local-suffix",
438+
"tel-extension",
439+
"email"
440+
]
441+
},
442+
"AutoFillCredentialField": {
443+
"name": "AutoFillCredentialField",
444+
"value": [
445+
"webauthn"
446+
]
355447
}
356448
}
357449
},
@@ -1422,6 +1514,42 @@
14221514
{
14231515
"name": "EventListenerOrEventListenerObject",
14241516
"overrideType": "EventListener | EventListenerObject"
1517+
},
1518+
{
1519+
"name": "OptionalPrefixToken",
1520+
"typeParameters": [
1521+
{
1522+
"name": "T extends string"
1523+
}
1524+
],
1525+
"overrideType": "`${T} ` | \"\""
1526+
},
1527+
{
1528+
"name": "OptionalPostfixToken",
1529+
"typeParameters": [
1530+
{
1531+
"name": "T extends string"
1532+
}
1533+
],
1534+
"overrideType": "` ${T}` | \"\""
1535+
},
1536+
{
1537+
"name": "AutoFillSection",
1538+
// Note: this will also eagerly match any invalid string
1539+
// after section- instead of stopping at the first whitespace.
1540+
// It should be something like /section-\S/ if it were supported.
1541+
"overrideType": "`section-${string}`"
1542+
},
1543+
{
1544+
"name": "AutoFillField",
1545+
"overrideType": "AutoFillNormalField | `${OptionalPrefixToken<AutoFillContactKind>}${AutoFillContactField}`"
1546+
},
1547+
{
1548+
// See the full list of possible autofill values for the `autocomplete` attribute:
1549+
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#determine-a-field's-category
1550+
// Full spec at https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#autofill.
1551+
"name": "AutoFill",
1552+
"overrideType": "AutoFillBase | `${OptionalPrefixToken<AutoFillSection>}${OptionalPrefixToken<AutoFillAddressKind>}${AutoFillField}${OptionalPostfixToken<AutoFillCredentialField>}`"
14251553
}
14261554
]
14271555
}

inputfiles/knownTypes.json

+11
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,15 @@
66
"AesGcmParams",
77
"AesKeyAlgorithm",
88
"AesKeyGenParams",
9+
"AutoFillBase",
10+
"AutoFillSection",
11+
"AutoFillAddressKind",
12+
"AutoFillNormalField",
13+
"AutoFillContactKind",
14+
"AutoFillContactField",
15+
"AutoFillField",
16+
"AutoFillCredentialField",
17+
"AutoFill",
918
"BigInteger",
1019
"ClientQueryOptions",
1120
"ClientTypes",
@@ -29,6 +38,8 @@
2938
"Keyframe",
3039
"MutationRecordType",
3140
"NamedCurve",
41+
"OptionalPrefixToken",
42+
"OptionalPostfixToken",
3243
"Pbkdf2Params",
3344
"PropertyIndexedKeyframes",
3445
"RsaHashedImportParams",

inputfiles/overridingTypes.jsonc

+21-1
Original file line numberDiff line numberDiff line change
@@ -1281,6 +1281,10 @@
12811281
"HTMLSelectElement": {
12821282
"properties": {
12831283
"property": {
1284+
"autocomplete": {
1285+
"name": "autocomplete",
1286+
"overrideType": "AutoFill"
1287+
},
12841288
"selectedOptions": {
12851289
"name": "selectedOptions",
12861290
"overrideType": "HTMLCollectionOf<HTMLOptionElement>"
@@ -1684,6 +1688,10 @@
16841688
"HTMLInputElement": {
16851689
"properties": {
16861690
"property": {
1691+
"autocomplete": {
1692+
"name": "autocomplete",
1693+
"overrideType": "AutoFill"
1694+
},
16871695
"selectionDirection": {
16881696
"name": "selectionDirection",
16891697
"overrideType": "\"forward\" | \"backward\" | \"none\""
@@ -1806,6 +1814,10 @@
18061814
"HTMLTextAreaElement": {
18071815
"properties": {
18081816
"property": {
1817+
"autocomplete": {
1818+
"name": "autocomplete",
1819+
"overrideType": "AutoFill"
1820+
},
18091821
"labels": {
18101822
"name": "labels",
18111823
"overrideType": "NodeListOf<HTMLLabelElement>"
@@ -2443,7 +2455,15 @@
24432455
"overrideIndexSignatures": [
24442456
"[index: number]: Element",
24452457
"[name: string]: any"
2446-
]
2458+
],
2459+
"properties": {
2460+
"property": {
2461+
"autocomplete": {
2462+
"name": "autocomplete",
2463+
"overrideType": "AutoFillBase"
2464+
}
2465+
}
2466+
}
24472467
},
24482468
"Blob": {
24492469
"methods": {

unittests/files/autocomplete.ts

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// @ts-expect-error should be a string
2+
document.body.getElementsByTagName("input")[0].autocomplete = false;
3+
// @ts-expect-error wrong value for this attribute
4+
document.body.getElementsByTagName("input")[0].autocomplete = "undefined";
5+
// @ts-expect-error does not accept boolean attributes
6+
document.body.getElementsByTagName("input")[0].autocomplete = "true";
7+
// @ts-expect-error does not accept boolean attributes
8+
document.body.getElementsByTagName("input")[0].autocomplete = "false";
9+
10+
// @ts-expect-error missing autofill token before webauthn
11+
document.body.getElementsByTagName("input")[0].autocomplete = "webauthn";
12+
13+
// @ts-expect-error wrong order for webauthn token
14+
document.body.getElementsByTagName("input")[0].autocomplete =
15+
"webauthn username";
16+
17+
// @ts-expect-error wrong order for contact specifier
18+
document.body.getElementsByTagName("input")[0].autocomplete = "tel mobile";
19+
20+
// @ts-expect-error off should be standalone
21+
document.body.getElementsByTagName("input")[0].autocomplete =
22+
"section-wrong off";
23+
24+
// @ts-expect-error on should be standalone
25+
document.body.getElementsByTagName("input")[0].autocomplete = "on username";
26+
27+
// @ts-expect-error home, work or mobile are only for contact tokens
28+
document.body.getElementsByTagName("input")[0].autocomplete = "mobile username";
29+
30+
// @ts-expect-error should probably be current-password or new-password
31+
document.body.getElementsByTagName("input")[0].autocomplete = "password";
32+
33+
document.body.getElementsByTagName("input")[0].autocomplete = "";
34+
document.body.getElementsByTagName("input")[0].autocomplete = "on";
35+
document.body.getElementsByTagName("input")[0].autocomplete = "off";
36+
document.body.getElementsByTagName("input")[0].autocomplete = "new-password";
37+
document.body.getElementsByTagName("input")[0].autocomplete =
38+
"current-password";
39+
document.body.getElementsByTagName("input")[0].autocomplete = "one-time-code";
40+
41+
document.body.getElementsByTagName("input")[0].autocomplete =
42+
"username webauthn";
43+
44+
document.body.getElementsByTagName("input")[0].autocomplete =
45+
"shipping street-address";
46+
document.body.getElementsByTagName("input")[0].autocomplete =
47+
"section-custom shipping street-address";
48+
49+
document.body.getElementsByTagName("input")[0].autocomplete = "work email";
50+
document.body.getElementsByTagName("input")[0].autocomplete =
51+
"section-custom billing mobile tel";
52+
53+
// @ts-expect-error only on and off are available on a form
54+
document.body.getElementsByTagName("form")[0].autocomplete = "new-password";
55+
56+
document.body.getElementsByTagName("form")[0].autocomplete = "off";
57+
58+
// @ts-expect-error autocomplete attribute is only for form elements
59+
document.body.getElementsByTagName("p")[0].autocomplete = "off";

0 commit comments

Comments
 (0)