简体   繁体   中英

TypeScript functional destructuring not enforcing default type

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:

  1. Why TypeScript doesn't tell me that 'notABoolean' isn't a boolean .
  2. How I can constrain the default value type without having to duplicate the FuncParams typing.

Reproduction on TS Playground

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';

Playground link

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM