'TypeScript: Partially "Partial" type

Environment

TypeScript's version is 3.2.1 and "tsconfig.json" is like below.

{
  "compilerOptions": {
    "target": "es5",
    "module": "commonjs",
    "strict": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": true,
    "esModuleInterop": true
  }
}

Question

I'm looking for Partially "Partial" type in TypeScript.

type Entity = {
  a: string,
  b: string,
  c?: string,
};

type Ham = MyType<Entity, 'b'>;
/**
 * expected to equal
 * {
 *   a: string,
 *   b?: string, // changed to be optional
 *   c?: string,
 * };
 */

P.S. Titian and t7yang

Thank you for your replies. I checked your types then both types pass compiler's check!

const abc = { a: 'a', b: 'b', c: 'c' };
const ab = { a: 'a', b: 'b' };
const ac = { a: 'a', c: 'c' };
const a = { a: 'a' };

// by t7yang
let test1Abc: OptionalKey<Entity, 'b'> = abc;
let test1Ab: OptionalKey<Entity, 'b'> = ab;
let test1Ac: OptionalKey<Entity, 'b'> = ac;
let test1A: OptionalKey<Entity, 'b'> = a;

// by Titian Cernicova-Dragomir    
let test2Abc: PickPartial<Entity, 'b'> = abc;
let test2Ab: PickPartial<Entity, 'b'> = ab;
let test2Ac: PickPartial<Entity, 'b'> = ac;
let test2A: PickPartial<Entity, 'b'> = a;


Solution 1:[1]

You can use Pick in conjunction with Partial to pick only the properties you want to make optional, while preserving the rest using Exclude to get the keys excluding the ones passed in to make optional :

type Entity = {
   a: string,
   b: string,
   c?: string,
};

type PickPartial<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>> & Partial<Pick<T, K>> 
type Ham = PickPartial<Entity, 'b'>; // a, b? , c?

Solution 2:[2]

type Entity = {
  a: string,
  b: string,
  c?: string,
};

type OptionalKey<T, O extends keyof T> = Pick<T, Exclude<keyof T, O>> & Partial<{ [P in O]: T[P] }>;

const a: OptionalKey<Entity, 'b'> = {
  a: 'a',
}

const ab: OptionalKey<Entity, 'b'> = {
  a: 'a',
  b: 'b'
}

const ac: OptionalKey<Entity, 'b'> = {
  a: 'a',
  c: 'c'
}

The idea is pick all the properties that want to make optional, then merge with the type than we want to make the property optional.

You can check this in typescript playground

Solution 3:[3]

Real simple solution:

type PickPartial<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
type PartialExcept<T, K extends keyof T> = Pick<T, K> & Partial<Omit<T, K>>;

Titian's solution was written before Typescript 3.5, which added the Omit helper.

Also remember that you can use string unions to pick multiple attributes to be made optional:

type Full = {
  a: string;
  b: string;
  c: string;
}

// These are equivalent
type ARequired = PickPartial<Full, 'b' | 'c'>;
type ARequired = PartialExcept<Full, 'a'>;

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Titian Cernicova-Dragomir
Solution 2 t7yang
Solution 3 iFreilicht