#Why returned types in function are flexible on adding extra properties for objects

20 messages · Page 1 of 1 (latest)

iron nimbus
#

I'm not quite sure why a function type definition is not strict when comparing the return type specified when it is an object and will allowed to include extra properties within it, despite the fact I won't be able to use those properties later.

This is something more related to the core of why TS works this way. I will not expect a solution here, but to understand why the TS team decides to make this behaves this way and also if there is any way at the tsconfig level where I can change this behavior, or in the type function level. I know that I can retype the return value when using the function and it will make an strict check but my question surrounds more the WHY THIS.

https://www.typescriptlang.org/play?ssl=33&ssc=18&pln=1&pc=1#code/JYOwLgpgTgZghgYwgAgKoGdrIN4ChnLAAmAXMiAK4C2ARtANz7IWZQhxURnphSgDmjAL65cCAPYgezVgEYyGLAF4cTAsTKyANGpnR2nMgCIW0WUYIFcIsZOmmoAJmQlFUZCryXCpZI53eDgZcyCasjkYBlroA9DEAksgIcCAg4mDIAA5w6OjIKcgQAB68cFlQ4pnIABbQELExAAJg6AC0xZkQCGDtUBVQutm5AO7iUL5G6OKcrUPoo+MW0TZgAJ6dyADiEGBuHsgAFACUHgB8aKyiElIZ-DtuJNu7rPvHZ4delhrIAMxRBEEOCEwtAfpEGolkiAsjk8gViqVypUanVdCl0rUoJkKpljMNqqtWnAADbE8TDCBEJZWIRHK52DIOH77O7PaDHURxSEpNIZBYAa2QYGqcAyCKgZWxyMx9TizTaHS6PWg-VwTIAdOjhdApZkgA

half prairieBOT
#

@iron nimbus Here's a shortened URL of your playground link! You can remove the full link from your message.

denik1981#5029

Preview:```ts
interface User {
id: number;
username: string;
}

const user1: User = {
id: 1,
username: "user1"
}

const user2 :User = {
id: 2,
username: "user2",

//I cannot pass an extra prop here
//@ts-expect-error
password: "some-password"    

}

type GetUser = () => User

const getUser:GetUser = () => ({
id: 3,
username: "user3",
//I can pass an extra prop here
anotherprop: "why-allowed"
})

const user3 = getUser()

//I cannot work that extra prop here
//@ts-expect-error
...```

eternal jasper
#

@iron nimbus the function definition is strict, but you don't have a type annotation on the function definition - but rather on the variable. typescript infers a type for the function because it lacks type annotations, then checks that it matches GetUser

#

doing getUser: GetUser doesn't help typescript's inference, but rather overwrites the type (as long as it's compatible)

#

if you add a type annotation on the function itself you get the same behavior as with the object literal

half prairieBOT
#
n_n#2622

Preview:```ts
interface User {
id: number
username: string
}

const getUser = (): User => ({
id: 3,
username: "user3",
//I can pass an extra prop here
anotherprop: "why-allowed",
})```

eternal jasper
#

!ts

half prairieBOT
#
interface User {
  id: number;
  username: string;
}

const getUser = (): User => ({
    id: 3,
    username: "user3",
    //I can pass an extra prop here
    anotherprop: "why-allowed"    
//  ^^^^^^^^^^^^^^^^^^^^^^^^^^
// Type '{ id: number; username: string; anotherprop: string; }' is not assignable to type 'User'.
//   Object literal may only specify known properties, and 'anotherprop' does not exist in type 'User'.
})```
iron nimbus
eternal jasper
#

extends is the same thing as "matches" that type

iron nimbus
#

I don't get why this is useful. It is rather confusing to me

iron nimbus
eternal jasper
#

typescript is structurally typed (and has width subtyping), so all { foo: number } means is that it's an object that has a property named foo of type number

iron nimbus
#

My questions is more related to why the TS team thought this was a useful behavior.
Check that despite I can add extra props, I cannot check against them once I use that function and get a new user. This confusses me because at runtime I have that extra prop available to being used for me but typescript denies it to me to be used which is absolutely fine because it matches the Fn definition instead of the inferred return type. But why TS is strict when using the function than when implementing the function. Not clear to me

eternal jasper
half prairieBOT
eternal jasper
#

!ts

half prairieBOT
#
interface User {
  id: number;
  username: string;
}

const getUser = () => ({
    id: 3,
    username: "user3",
    //I can pass an extra prop here
    anotherprop: "why-allowed"    
})

const what = getUser().anotherprop
//    ^? - const what: string```
eternal jasper
#

the reason why the type information is being lost is because you're effectively overwriting the type of the function

past belfry
#

FWIW, I do think TS could probably add a special case where excess property checking is applied to function literals with type annotations on them.