2

Based on Scott Wlaschin examples in F#, I'm trying to design a functional domain model using Typescript.

But, in order to have more strictly defined types for optional params, I've noticed that typescript is not validating properties I expected it would.

For example, for this types:

type ObjectOne = {
    propertyOfOneMustBeNumber: number
}
type ObjectTwo = {
    propertyOfTwoMustBeNumber: number
}

type Options = ObjectOne | ObjectTwo

I have the following use cases:

// valid
const test1: Options = {
    propertyOfOneMustBeNumber: 1,
    propertyOfTwoMustBeNumber: 2
}

// invalid:
// Object literal may only specify known properties,
// and 'unrecognized_property' does not exist in type 'Options'.
const test2: Options = {
    propertyOfOneMustBeNumber: 1,
    unrecognized_property: 'string'
}

// valid
const test3: Options = {
    propertyOfOneMustBeNumber: 1,
    propertyOfTwoMustBeNumber: 'string'
}

I understand why there was an error for a test2 variable, but why there are no erros for test3 variable, even though propertyOfTwoMustBeNumber is a string and not a number?

This is also valid in Flow type system.

Ryan Cavanaugh
  • 209,514
  • 56
  • 272
  • 235
domagojk
  • 1,020
  • 2
  • 11
  • 25
  • 1
    duplicate of: https://stackoverflow.com/questions/31816061/why-am-i-getting-an-error-object-literal-may-only-specify-known-properties ? – Webbies Sep 15 '17 at 16:43
  • 1
    You misunderstand what a sum type is. It's either/or, not both. An `Option` (also sometimes called `Maybe`) is a `Just something` or `null`, not both. – Jared Smith Sep 15 '17 at 16:54
  • Possible duplicate of [Why am I getting an error "Object literal may only specify known properties"?](https://stackoverflow.com/questions/31816061/why-am-i-getting-an-error-object-literal-may-only-specify-known-properties) – Jared Smith Sep 15 '17 at 16:55
  • **Not** a duplicate – Ryan Cavanaugh Sep 15 '17 at 16:56
  • Your notion of an union type is actually a product type. Functional data types are usually expressed as unions of products. You might want to look into algebraic data types. –  Sep 15 '17 at 17:53

1 Answers1

1

According to both type systems, test3 is a valid subtype of ObjectOne.

In either type system you can use a discriminating property:

type ObjectOne = {
    kind: 'one';
    propertyOfOneMustBeNumber: number
}
type ObjectTwo = {
    kind: 'two';
    propertyOfTwoMustBeNumber: number
}

type Options = ObjectOne | ObjectTwo

// error
const test2: Options = {
    kind: 'one',
    propertyOfOneMustBeNumber: 1,
    unrecognized_property: 'string'
}

// error
const test3: Options = {
    kind: 'two',
    propertyOfOneMustBeNumber: 1,
    propertyOfTwoMustBeNumber: 'string'
}

In flowtype you can also use an Exact Type, which will prevent extra properties of any kind from existing:

type ObjectOne = {|
    propertyOfOneMustBeNumber: number
|}

In TypeScript you can exclude the property from existing in the counterpart type using an optional never property:

type ObjectOne = {
  propertyOfOneMustBeNumber: number;
  propertyOfTwoMustBeNumber?: never;
}
type ObjectTwo = {
  propertyOfOneMustBeNumber?: never;
  propertyOfTwoMustBeNumber: number;
}
Ryan Cavanaugh
  • 209,514
  • 56
  • 272
  • 235
  • I'm not necessarily disagreeing (I don't know typescript) but `Option` is F#'s version of `Maybe` which should either be `Just a` or `Nothing`. I'm not sure the OP understood the video being referenced. – Jared Smith Sep 15 '17 at 16:59