Summary
<fluent-text-input>'s spellcheck attribute uses a converter whose toView
is not null-safe, so any reflection of spellcheck (setting spellcheck="false",
or SSR-hydrating an element that carries it) throws
TypeError: Cannot read properties of null (reading 'toString'). In practice
spellcheck is unusable on the component.
Root cause
packages/web-components/src/text-input/text-input.base.ts:
@attr({
converter: {
fromView: value => (typeof value === 'string' ? ['true', ''].includes(value.trim().toLowerCase()) : null),
toView: value => value.toString(),
},
})
public spellcheck!: boolean;
spellcheck has no default → the backing field is undefined.
toView calls .toString() directly → throws on null/undefined.
fromView returns null for any non-string input, which can feed null
straight back into the field and into toView.
Steps to reproduce
Minimal standalone repro (loads the component from esm.sh, no build/install):
<fluent-text-input id="a" spellcheck="false">Case A</fluent-text-input>
<script type="module">
import 'https://esm.sh/@fluentui/web-components@3.0.0-rc.14/dist/esm/text-input/define.js';
await customElements.whenDefined('fluent-text-input');
// Case B: enqueues attribute reflection -> converter.toView(null) -> null.toString()
const b = document.createElement('fluent-text-input');
document.body.appendChild(b);
queueMicrotask(() => { b.spellcheck = null; });
</script>
Serve over http (custom elements need a real origin) and open DevTools console.
Versions
- Reproduces on
@fluentui/web-components@3.0.0-rc.14.
- The same code is present on
main (HEAD) — text-input.base.ts still has the
identical converter and no default value.
Expected
Setting or reflecting spellcheck (including the default/unset state) does not
throw; <fluent-text-input spellcheck="false"> hydrates and disables spelling
suggestions on the internal <input>.
Actual
TypeError: Cannot read properties of null (reading 'toString')
at Object.toView (.../@fluentui/web-components/dist/esm/text-input/text-input.base.js:417)
at .../@microsoft/fast-element/dist/esm/components/attributes.js:151
at tryRunTask (.../@microsoft/fast-element/dist/esm/observation/update-queue.js:19)
Suggested fix
Make the converter null-safe and give the attribute a boolean default, e.g.:
converter: {
fromView: value => (typeof value === 'string' ? ['true', ''].includes(value.trim().toLowerCase()) : false),
toView: value => String(!!value),
},
Any equivalent that avoids calling .toString() on null/undefined works.
Summary
<fluent-text-input>'sspellcheckattribute uses a converter whosetoViewis not null-safe, so any reflection of
spellcheck(settingspellcheck="false",or SSR-hydrating an element that carries it) throws
TypeError: Cannot read properties of null (reading 'toString'). In practicespellcheckis unusable on the component.Root cause
packages/web-components/src/text-input/text-input.base.ts:spellcheckhas no default → the backing field isundefined.toViewcalls.toString()directly → throws onnull/undefined.fromViewreturnsnullfor any non-string input, which can feednullstraight back into the field and into
toView.Steps to reproduce
Minimal standalone repro (loads the component from esm.sh, no build/install):
Serve over http (custom elements need a real origin) and open DevTools console.
Versions
@fluentui/web-components@3.0.0-rc.14.main(HEAD) —text-input.base.tsstill has theidentical converter and no default value.
Expected
Setting or reflecting
spellcheck(including the default/unset state) does notthrow;
<fluent-text-input spellcheck="false">hydrates and disables spellingsuggestions on the internal
<input>.Actual
Suggested fix
Make the converter null-safe and give the attribute a boolean default, e.g.:
Any equivalent that avoids calling
.toString()onnull/undefinedworks.