Typescript: Partial<T> make only some properties optional

Created on 18 Jul 2018  Β·  7Comments  Β·  Source: microsoft/TypeScript

Search Terms

Suggestion

Use Cases


current Partial<T> will make all properties of T optional
I want to specify only some properties of T optional

Examples

interface Message {
    mid: number;
    content: string;
}

interface MessageOptional {
   mid?: number;
   content: string;
}

const message: Message = assembleMessage({ content: 'hello' })

function assembleMessage(message: MessageOptional) : Message{
    const mid =  ... // generate mid somehow
    message.mid = mid
    return message
} 

it's very often sometimes we want to omit some properties of an object and later supplement it.
currently typescript has concepts like Intersection、Union, how about Complement?

Checklist

My suggestion meets these guidelines:

  • [x] This wouldn't be a breaking change in existing TypeScript / JavaScript code
  • [x] This wouldn't change the runtime behavior of existing JavaScript code
  • [x] This could be implemented without emitting different JS based on the types of the expressions
  • [x] This isn't a runtime feature (e.g. new expression-level syntax)
Question

Most helpful comment

This works for me

type Optional<T, K extends keyof T> = Omit<T, K> & Partial<T>;

Playground link

All 7 comments

You can use existing Partial and Exclude to implement an approach like this:

type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
type WithOptional<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;

interface Foo {
    foo: string;
    bar: number;
    baz: boolean;
}
// { foo?: string; bar: number; baz?: boolean; }
type OptionalFoo = WithOptional<Foo, 'foo' | 'baz'>;

const createFoo = (base: OptionalFoo): Foo => {
  return { foo: 'foo', baz: true, ...base };
};
const optionalFoo: OptionalFoo = { foo: '???', bar: 2 };
const fullFoo = createFoo(optionalFoo); // { foo: '???', bar: 2, baz: true }

@0x414c
thanks anyway, I hope Omit and WithOptional can be added to lib.d.ts in the future cause it's really a very common need.
yet there's another problem with keyof a Union type

interface MessageA {
   mid: number;
   type: number;
   text: string;
}

interface MessageB {
   mid: number;
   type: number;
   url: string;
}
type Message = MessageA | MessageB

type Keys = keyof Message    //    'mid' | 'type'

/**
*  {
*       mid?: number;
*       type: number; 
*  }
*/
type OptionalMessage = WithOptional<Message, 'mid'> 

// what I really what :
type OptionalMessageNeed = WithOptional<MessageA, 'mid'> | WithOptional<MessageB, 'mid'> 

I just got a solution.

type AllKeyOf<T> = T extends never ? never : keyof T

type Omit<T, K> = { [P in Exclude<keyof T, K>]: T[P] }

type Optional<T, K> = { [P in Extract<keyof T, K>]?: T[P] }

type WithOptional<T, K extends AllKeyOf<T>> = T extends never ? never : Omit<T, K> & Optional<T, K>

/**
  {
       mid?: number;
       type: number;
       text?: string; 
  }
| {
       mid?: number;
       type: number;
       url: string;
  }    
*/
type a = WithOptional<Message, 'mid' | 'text'>

Automatically closing this issue for housekeeping purposes. The issue labels indicate that it is unactionable at the moment or has already been addressed.

Guys those solutions are unredable. I think it's better to create separate interface.

This works for me

type Optional<T, K extends keyof T> = Omit<T, K> & Partial<T>;

Playground link

This works for me

type Optional<T, K extends keyof T> = Omit<T, K> & Partial<T>;

Playground link

This is really good. However, is there a way to keep the keys ordered as the original type?

Was this page helpful?
0 / 5 - 0 ratings

Related issues

siddjain picture siddjain  Β·  3Comments

CyrusNajmabadi picture CyrusNajmabadi  Β·  3Comments

Roam-Cooper picture Roam-Cooper  Β·  3Comments

Antony-Jones picture Antony-Jones  Β·  3Comments

kyasbal-1994 picture kyasbal-1994  Β·  3Comments