1

I have this interface .

interface parts {
    general: boolean,
    source: boolean,
    target: boolean
}

When init state in Parent component and type it with that interface, TypeScript works as expected.

const Parent: React.FC = () => { 
    const [parts, setParts] = useState<parts>({
        general: false,
        source: false,
        target: false,
        wrongKey: "Typescript reports problem"
    })
    
    return (
        <Child setParts={setParts} />
    )
}

But when it pass to child, TypeScript doesn't work as expected.

const Child: React.FC<{setParts: React.Dispatch<React.SetStateAction<parts>> }> =({setParts}) => { 
    setParts((parts: parts) => {
        ...parts,
        wrongKey: "Typescript doesn't report problem"
    })

    return (
        <div></div>
    )
}

With types, like boolean or string, it works as should, but there is some problem with dictionary.

3
  • 1
    What do you mean by "doesn't work as expected"? Also you say the first one works as expected but in that code block you have wrongKey: "Typescript reports problem", then you say second one doesn't work but you have wrongKey: "Typescript doesn't report problem" in that second code block. Think they're mixed up Commented Apr 12, 2022 at 9:15
  • You are overriding your type by adding a wrongKey, since there is already a typescript problem in the parent file, when you again give wrongKey in child, it won't report error unless the prior is addressed. Remove wrongKey from parent and the error will pop! Commented Apr 12, 2022 at 9:17
  • @RitikBanger It doesn't working. Key "wrongKey" is just example, in reality in Parent component I have just keys which are allowed and when I typed in child component I made misstake which made me think "Why ts wasn't yelling at me?" Commented Apr 12, 2022 at 9:47

1 Answer 1

1

Excess property check only triggered when you try to assign an object literal. For other cases TypeScript only checks if the shape of object matches with the requested type. The reasoning is that it is most likely a developer error when you pass a wrong shaped object inlined. What could be the benefit to allow that? So it is a typo or you want something else.

But if you pass a variable, it only has to check if shape is ok, there can't be really any runtime issue. You can do, for example, the following:

 const [parts, setParts] = useState<parts>({
        general: false,
        source: false,
        target: false,
        wrongKey: "Typescript reports problem"
    } as parts)

That way TypeScript will make sure you pass 'something' which can be shaped as parts. Don't know if it is useful, tho, consider it just as an example:)

I'm not sure if this is really an issue that you are not aware about passing an extraneous property. I would like to stress if you forgot to pass a property to satisfy the shape of the required type, TypeScript will tells you that, and that is what important.

If you really want to deny extra properties, please check this answer: Is it possible to restrict TypeScript object to contain only properties defined by its class?

UPDATE 1: Why excess property check is not triggered, as actually we return with an object literal and I said excess property check run in those case, this is kinda a contradiction, but not, actually.

Consider this example:

type Parts = {
general: boolean;
source: boolean;
target: boolean;
}

type FunctionType = (parts: Parts) => Parts;

function ShowsError(parts: Parts): Parts {
 return {
   ...parts,
   someExtraPropertyWhichTriggerError:'Ooops'
 }
}

const myArrowFunction = (parts: Parts) => ({
  ...parts,
  someExtraPropertyWithoutError:'Why no report?:('
});


const DoesntShowError: FunctionType = myArrowFunction;

So what is happening there? Looks like two identical function, one is arrow, one is normal, why there is no error in case of arrow function? Same arguments, same return type, same object returned.

The key difference is, when we define our arrow function its return statement NOT contextually binded to Parts. TypeScript can't know where it is going to be used. So its generate a shape for its return type, and go on. Then we assign it to DoesntShowError which requires us assign a function with a given type. And the arrowfunction satisfy that requirement. Thats all, I think, hope it helps.

Sign up to request clarification or add additional context in comments.

4 Comments

You can check that he has given the type of setState to take only parts as an interface. setParts: React.Dispatch<React.SetStateAction<parts>>
Yes, the callback function's return type will be parts, while he is returning an object containing an extra property. And it is not an issue from TypeScript's point of view, error is not reported in that case.
That's the question only why it is not an issue from TypeScript's point of view, error is not reported in that case.
Because excess property check is not triggered. This is really strange, but works as intended, I will update my answer shortly to show where do we 'lost' the error actually.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.