The following TypeScript type:
type Errors = ({
err1: false;
} | {
err1: true;
msg1: string;
}) & ({
err2: false;
} | {
err2: true;
msg2: string;
})
Which is basically a intersection of two discriminated unions, works as expected in plain TypeScript:
const customError: Errors = {
err1: false,
err2: false,
attributeThatShouldntBeAllowed: 1 // It gives an IDE error
}
However, when it comes to React, particularly with useState hook:
const [errors, setErrors] = useState<Errors>({
err1: false,
err2: false
}) // proper initialization with Errors type, nothing wrong here
It becomes not a little bit strict about the type:
setErrors(prevState => ({
...prevState,
attributeThatShouldntBeAllowed: "lol",
anotherAttribute: 1
})) // Doesn't produce any errors
What exactly happens here?
As described, I had this issue in my current project and I’ve just created a basic sandbox. This is it’s tsconfig.json
:
{
"compilerOptions": {
"target": "es2016",
"module": "commonjs",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true
}
}
Also, here it is the tsconfig.json
of my current project:
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"strict": true,
"jsx": "react",
"allowSyntheticDefaultImports": true,
"baseUrl": ".",
"paths": {
"@domain/*": ["./src/domain/*"],
"@infra/*": ["./src/infra/*"],
"@types/*": ["./src/types/*"],
"@controllers/*": ["./src/controllers/*"]
}
},
"include": ["src", "node_modules"],
}
2
Answers
Apparently, it is a normal behaviour of the structural type in TypeScript, that, considering a generic object type T, an object that has every attribute of T and any aditional attributes is assignable to T. If I explicitly determine the return function of setState, like this:
it works as expected.
TypeScript type compatibility
Don't widen return types of function expressions #241
Typescript: why not strict return type?
You may simplify your type. Try this:
As per your setState: