I am rather new to typescript and are trying to assign the correct types to my JS files. While doing so, I am trying to write as view boilerplate as possible and so I stumbled over this:
I receive compilation errors only under specific conditions. As soon as I call the array filter method after line 14, it starts failing to compile but doesn't fail at line 17.
Why does it fail when calling filter and why does it not fail in line 17?
interface A {
iAm: 'a';
}
interface B {
iAm: 'b';
}
interface C {
iAm: string;
}
const fails: (A|B)[] = [{iAm: 'a'}, {iAm: 'b'}]
.filter(() => true);
const works: (B|C)[] = [{iAm: 'b'}, {iAm: 'c'}]
.filter(() => true);
Error:
const fails: (A | B)[]
Type '{ iAm: string; }[]' is not assignable to type '(A | B)[]'.
Type '{ iAm: string; }' is not assignable to type 'A | B'.
Type '{ iAm: string; }' is not assignable to type 'B'.
Types of property 'iAm' are incompatible.
Type 'string' is not assignable to type '"b"'.(2322)
CodePudding user response:
This is because typescript is inferring {iAm: 'a'}
as type {iAm: string}
.
To tell typescript that you mean the literal string 'a'
you can use "as const".
Your code example will become.
interface A {
iAm: 'a';
}
interface B {
iAm: 'b';
}
interface C {
iAm: string;
}
const fails: (A|B)[] = [{iAm: 'a'}, {iAm: 'b'}]
.filter((x) => {
return true
});
const failsButNowWorks: (A|B)[] = [{iAm: 'a' as const}, {iAm: 'b' as const}]
.filter((x) => {
return true
});
const works: (B|C)[] = [{iAm: 'b'}, {iAm: 'c'}]
.filter(() => true);
I believe it is the .filter
that is causing this conversion since you can technically modify the value in a .filter
For example
const works: (B|C)[] = [{iAm: 'b'}, {iAm: 'c'}]
.filter((x) => {
x.iAm = 'asdf'
return true
});
But if you use "as const" it will infer it correctly and cause a type error.
const failsButNowWorks: (A|B)[] = [{iAm: 'a' as const}, {iAm: 'b' as const}]
.filter((x) => {
// Type Error here since iAm now has type `{iAm: 'a' | 'b' }`
x.iAm = 'asdf'
return true
});