Reputation: 23
I have a function which accepts a single object parameter as follows. (Note, this is a contrived example but it demonstrates the problem.)
type FuncParams = { sayYes: boolean; }
type FuncType = (params: FuncParams) => string;
const maybeSayYes: FuncType = ({ sayYes }) => sayYes ? 'Yes' : 'No';
maybeSayYes({ sayYes: true }); // Returns 'Yes'
maybeSayYes({ sayYes: false }); // Returns 'No'
I want to make the sayYes
property optional, so I updated the types as follows and added a default to the function declaration:
type FuncParams = { sayYes?: boolean; }
type FuncType = (params: FuncParams) => string;
const maybeSayYes: FuncType = ({ sayYes = true }) => sayYes ? 'Yes' : 'No';
maybeSayYes({ sayYes: true }); // Returns 'Yes'
maybeSayYes({ sayYes: false }); // Returns 'No'
maybeSayYes({}); // Returns 'Yes'
However, I noticed that I can assign any default value to sayYes
and TypeScript doesn't flag up the incorrect type. For example:
const maybeSayYes: FuncType = ({ sayYes = 'notABoolean' }) => sayYes ? 'Yes' : 'No';
I expected TypeScript to infer the boolean
type from FuncType
and stop me assigning a string
, but it doesn't.
I can get the error to show if I explicitly type the parameter as follows:
const maybeSayYes: FuncType = ({ sayYes = 'notABoolean' }: FuncParams) => sayYes ? 'Yes' : 'No';
However, I assumed TypeScript would infer this type from FuncType
. I'm sure it's my understanding of TypeScript that's at fault, but I'd like to know:
'notABoolean'
isn't a boolean
.FuncParams
typing.Upvotes: 2
Views: 101
Reputation: 1074335
I'm surprised by the behavior you describe, but my knowledge of TypeScript is still not very deep.
You can do it using a function overload type:
type FuncParams = { sayYes?: boolean; }
type FuncType = {
(params: FuncParams): string;
(params: FuncParams & {sayYes: never}): string; // Or just: {params: {sayYes: never}: any;
};
Then, a call that tries to use sayYes
with a non-boolean
type will match the second overload and cause an error. (Many thanks to jcalz for pointing out that sayYes
can remain optional, and that you can use a simpler signature for he never
overload.)
That gives you this behavior:
// Correct default value type
const maybeSayYes1: FuncType = ({ sayYes = true }) => sayYes ? 'Yes' : 'No';
// Incorrect default value type
const maybeSayYes2: FuncType = ({ sayYes = 'notABoolean' }) => sayYes ? 'Yes' : 'No';
// ^−−−−−−−−−−−−− TypeScript compiler error here
// Correctly don't have it at all (since it's optional)
const maybeSayYes3: FuncType = ({ }) => Math.random() ? 'Yes' : 'No';
Upvotes: 3