2

In the following example im trying to figure out why my typing works for all parts of my object apart from my reducer return type?

If i explicitly set: reducer: (state, action): CounterState the compiler complains (as expected) that i'm not returning the right state. The thing is, i don't see why i should have to do this seeing as i'm already enforcing this within my Config type??

The simplified example:

interface CounterState {
    counter: number;
}

type Reducer = () => CounterState

const reducer1: Reducer = () => ({
    counter: 1,
    foo: 'bar' // no errors, why?
})

const reducer2: Reducer = (): CounterState => ({
    counter: 1,
    foo: 'bar' // error: Object literal may only specify known properties
})

Samuel
  • 2,485
  • 5
  • 30
  • 40
  • May be the better title would be something like: "TypeScript: excess properties are not checked in lambdas without explicit return type"? You cannot remove `counter` from the object literal, the problem with excess properties only. – Valeriy Katkov Jul 26 '19 at 09:36
  • Thanks @ValeriyKatkov i have accepted your changes and updated the title. – Samuel Jul 26 '19 at 09:41
  • @Samuel also this doesn't seem to have anything to do with lambdas in particular, does it? – Tomek Oct 19 '19 at 17:54

2 Answers2

1

Finally I found the issue in GitHub, exactly about the problem. In short:

Ideally this would be an error. Unfortunately it turns out to be very difficult to fix this without possibly having consequences in terms of runaway recursion and/or performance

Original answer: Since typescript 1.6, object literals mustn't have extra properties. But if you cast an object to the type, extra properties are allowed. For example:

const state: CounterState = {
    counter: 1,
    foo: "bar" // Error, unknown property 'foo'
};

const state2 = {
    counter: 1,
    foo: "bar" // no errors
} as CounterState

It looks very similar to your problem, when you specify the lambda return type explicitly, the first rule is applied. But, if the return type isn't specified, the compiler thinks: "ok, may be I can cast the object to the CounterState... Is it ok? I'm not sure... But, I will try!", and the second rule is applied.

But I cannot refer to any documentation or compiler specification, which describes such behaviour, I didn't found it too.

Valeriy Katkov
  • 33,616
  • 20
  • 100
  • 123
0

Type compatibility in TypeScript is based on structural subtyping.

https://www.typescriptlang.org/docs/handbook/type-compatibility.html

Typescript designed to allow extra properties. But in some places we have a little inconsistent behaviour and it claims object literal may only specify known properties. Such behaviour is more expected but it is not a structural subtyping...

https://medium.com/@KevinBGreene/surviving-the-typescript-ecosystem-interfaces-and-structural-typing-7fcecd54aef5

Yozi
  • 11,435
  • 1
  • 22
  • 25
  • The topic is a little complex, so I refer to this 2 articles instead of trying to explain the things myself in a few words – Yozi Jul 26 '19 at 07:40
  • Thanks! so typescript allows additional properties not defined in your return Type on objects when you return an object literal. Presumably the only way around this is to explicitly state the return type on the function itself to prevent TS from inferring it as a subtype? – Samuel Jul 26 '19 at 08:08
  • Yes, you have to set it explicitly – Yozi Jul 26 '19 at 09:55