19

How can I describe type like

key is optional, but if it existed, it cannot be undefined.

At the first time, I'm using Partial to make all properties of T optional.

interface A {
  a: number;
}

var data: A = {
  a: 1,
}

var update1: Partial<A> = {

}

var update2: Partial<A> = {
  a: undefined
}

var result1: A = {
  ...data,
  ...update1,
} // { a: 1 }

var result2: A = {
  ...data,
  ...update2,
} // { a: undefined }

The problem here is that result2 is not implementing interface A in runtime, but typescript never complains about it. Is it bug or feature? I guess typescript not working well with spread operator...

The goal is distinguish these two variable using typescript!


var data: T = {
  a: 1,
  b: 2,
}

var optional1: MyPartial<T> = { // Totally OK

}

var optional2: MyPartial<T> = { // OK
  a: 3,
}

var optional3: MyPartial<T> = { // OK
  b: 4,
}

var optional4: MyPartial<T> = { // OK
  a: 3,
  b: 4,
}

var undef1: MyPartial<T> = { // typescript must throw error
  a: undefined,
}

var undef2: MyPartial<T> = { // typescript must throw error
  a: 3,
  b: undefined
}

...

Check this out TypeScript playground example.

2
  • I don't believe it's possible, unfortunately, due to this (still) open issue: github.com/Microsoft/TypeScript/issues/13195; there are some workarounds depending on how exactly you really want to use it, but a general solution seems impossible at the moment. :-/ Commented Jan 18, 2019 at 23:02
  • this should really be supported. I like using Partial<T> for redux action creators so I don't need to include all of the props on the T object (e.g., setting part of a User type while registering), but this allows me to potentially set the wrong property type :( Commented Jan 19, 2020 at 22:07

3 Answers 3

14

Support now exists in 4.4.0 via --exactOptionalPropertyTypes

So by default, TypeScript doesn’t distinguish between a present property with the value undefined and a missing property ...

In TypeScript 4.4, the new flag --exactOptionalPropertyTypes specifies that optional property types should be interpreted exactly as written, meaning that | undefined is not added to the type:

https://devblogs.microsoft.com/typescript/announcing-typescript-4-4/#exact-optional-property-types

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

Comments

3

As @dtanabe mentioned, there isn't really a good solution here until TypeScript support (if they decide to). And main problem from doing it in user land is that in a var def (i.e. const x: Foo = ...) we do not have access to the type of the value. If you are OK changing your code from using spread to using a function you can do something like this:

interface A {
  a: number;
}

type EnsurePartial<TTarget, TUpdate extends keyof TTarget> =
  undefined extends TTarget[TUpdate]
  ? never
  : { [Key in TUpdate]: TTarget[Key] };

declare function update<T>(propsToUpdate: EnsurePartial<A, T>): void

update({}) // OK
update({ a: 42 }) // OK
update({ a: undefined }) // error
update({ c: undefined }) // error

1 Comment

there is error in this code: Type 'T' does not satisfy the constraint '"a"'.
1

There is an error in Dmitriy answer, here is the fixed version

interface A {
  a: number;
  b: boolean
}

type PartialWithoutUndefined<M,T> = M extends T
    ?  undefined extends M[keyof M]
            ? never
            : M
    : never


declare function update<M>(propsToUpdate: PartialWithoutUndefined<M,Partial<A>>): void

update({}) // OK
update({ a: 42 }) // OK
update({ a: undefined }) // error
update({ c: undefined }) // error

playground

weakness: it is impossible to type anything as undefined even if that is your intention

1 Comment

@DanielTate what do you mean?

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.