/**
 * Implement a `GetColor` generic that takes
 * a union of `LogStatus` string and return
 * the appropriate union of colors for
 * these statuses among "red", "orange" and "blue".
 */
namespace getColor {
  type LogStatus = "error" | "warning" | "info";
 
  type GetColor<Status extends LogStatus> = {
    error: "red";
    warning: "orange";
    info: "blue";
  }[Status];
 
  type res1 = GetColor<"error">;
  type test1 = Expect<Equal<res1, "red">>;
 
  type res2 = GetColor<"error" | "warning">;
  type test2 = Expect<Equal<res2, "red" | "orange">>;
 
  type res3 = GetColor<"warning" | "info">;
  type test3 = Expect<Equal<res3, "orange" | "blue">>;
 
  type res4 = GetColor<"error" | "warning" | "info">;
  type test4 = Expect<Equal<res4, "red" | "orange" | "blue">>;
}
 
/**
 * Implement an `AllValues` generic that
 * takes a union of objects, and returns
 * every possible value these objects can
 * hold inside any of their properties.
 */
namespace allValues {
  type AllValues<T> = T extends object ? T[keyof T] : never;
 
  type res1 = AllValues<{ a: "value a" }>;
  type test1 = Expect<Equal<res1, "value a">>;
 
  type res2 = AllValues<{ a: "value a" } | { b: "value b" }>;
  type test2 = Expect<Equal<res2, "value a" | "value b">>;
 
  type res3 = AllValues<{ a: string; b: number } | { b: boolean; c: bigint }>;
  type test3 = Expect<Equal<res3, string | number | boolean | bigint>>;
}
 

extends unknown ? 활용해서 분배.. 못풀었다.

 
/**
 * Lodash's `compact` function takes an array
 * that can contain "falsy values", and returns
 * an array with these values filtered out.
 * Write its generic type signature!
 *
 * Note: The list of "falsy" values is
 * false, null, 0, "", and undefined.
 */
namespace compact {
  type Falsy = false | null | 0 | "" | undefined 
  declare function compact<T>(
    list: T[]
  ): Exclude<T, Falsy>[];
 
  let res1 = compact([1, 2, null, 3, undefined, 4]);
  type test1 = Expect<Equal<typeof res1, number[]>>;
 
  let res2 = compact([undefined, 0 as const, "a", "", "b", "c", "d"]);
  type test2 = Expect<Equal<typeof res2, string[]>>;
 
  let res3 = compact([...([1, undefined, true, "", "hello", null] as const)]);
  type test3 = Expect<Equal<typeof res3, (1 | true | "hello")[]>>;
}
 

못풂..

/**
 * Lodash's `partition` function takes an array, a *type guard*
 * function, and returns a *tuple* containg *two arrays*.
 * - The left array contains elements passing the predicate
 * - the right array contains elements that didn't pass
 *
 * Type `partition` so that output arrays are narrowed
 * using the return type of the guard function.
 *
 * Note: Type guards are predicate functions that TypeScript
 * can use for type narrowing. their type signatures look like this:
 * `(param) => param is SomeType`.
 * Learn more: https://www.typescriptlang.org/docs/handbook/advanced-types.html#user-defined-type-guards
 */
namespace partition {
  declare function partition<T, P extends T>(
    list: T[],
    predicate: (value: T) => value is P
  ): [P[], Exclude<T,P>[]];
 
  const res1 = partition(
    [1, 2, "N/A", 7, "oops"],
    (x): x is number => typeof x === "number"
  );
  type test1 = Expect<Equal<typeof res1, [number[], string[]]>>;
 
  const res2 = partition(
    [true, false, 1, true, 0],
    (x): x is boolean => typeof x === "boolean"
  );
  type test2 = Expect<Equal<typeof res2, [boolean[], number[]]>>;
 
  const res3 = partition(
    ["value", "onChange", "onSubmit", "valid", "focused"],
    (x): x is `on${string}` => x.startsWith("on")
  );
  type test3 = Expect<Equal<typeof res3, [`on${string}`[], string[]]>>;
}
 
/**
 * Implement a `FromEntries` generic, transforming
 * a union of [key, value] entries into an object type.
 */
namespace fromEntries {
  type FromEntries<Entries extends [any, any]> = {
    [Entry in Entries as Entry[0]]: Entry[1];
  };
 
  type res1 = FromEntries<["a", string]>;
  type test1 = Expect<Equal<res1, { a: string }>>;
 
  type res2 = FromEntries<["a", string] | ["b", number]>;
  type test2 = Expect<Equal<res2, { a: string; b: number }>>;
 
  type res3 = FromEntries<never>;
  type test3 = Expect<Equal<res3, {}>>;
}
 
/**
 * Implement a `PickByValue` generic than only keep
 * properties of `Obj` that are assignable to
 * the `Condition` type parameter.
 */
namespace pickByValue {
  type PickByValue<Obj, Condition> = FromEntries<
  Extract<
    Entries<Obj>,
    [any, Condition]
  >
>;
 
  /** Provided helper functions */
  type Entries<Obj> = {
    [K in keyof Obj]: [K, Obj[K]];
  }[keyof Obj];
 
  type FromEntries<Entries extends [any, any]> = {
    [Entry in Entries as Entry[0]]: Entry[1];
  };
 
  /** Unit tests */
  type res1 = PickByValue<{ a: 1; b: 2; c: undefined }, number>;
  type test1 = Expect<Equal<res1, { a: 1; b: 2 }>>;
 
  type res2 = PickByValue<{ age: 22; name: "Alice"; bio: string }, string>;
  type test2 = Expect<Equal<res2, { name: "Alice"; bio: string }>>;
 
  type res3 = PickByValue<{}, string>;
  type test3 = Expect<Equal<res3, {}>>;
}
 
/**
 * Implement a `MakeEnum` generic taking a union of strings
 * and returning an object with a `key: value` pair for
 * each element in the union. 
 * Keys and values should be the same.
 */
namespace makeEnum {
 
  type MakeEnum<Props extends string> = {
    [K in Props]: K
  }
 
  type res1 = MakeEnum<"red" | "green" | "blue">;
  type tes1 = Expect<Equal<res1, { red: "red"; green: "green"; blue: "blue" }>>;
 
  type res2 = MakeEnum<"cat">;
  type tes2 = Expect<Equal<res2, { cat: "cat" }>>;
 
  type res3 = MakeEnum<never>;
  type tes3 = Expect<Equal<res3, {}>>;
}
 
/**
 * The `groupProperties` function takes a homogenous
 * list of objects, and returns a single object
 * where all properties contain lists of values.
 * Make it generic!
 */
namespace groupProperties {
  declare function groupProperties<T>(args: T[]): ValuesToArrays<T>;
 
  type ValuesToArrays<T> = {
    [K in keyof T]: T[K][]
  };
 
  const input1 = [{ x: 0, y: 0 }, { x: 12, y: 2 }, { x: 7, y: -2 }];
  const res1 = groupProperties(input1);
  type test1 = Expect<Equal<typeof res1, { x: number[]; y: number[] }>>;
 
  const input2 = [
    { title: "🧐", createdAt: 1678282179 },
    { title: "🔥", createdAt: 1678283456 }
  ];
  const res2 = groupProperties(input2);
  type test2 = Expect<
    Equal<typeof res2, { title: string[]; createdAt: number[] }>
  >;
 
  const input3 = [{}, {}, {}];
  const res3 = groupProperties(input3);
  type test3 = Expect<Equal<typeof res3, {}>>;
}
 
/**
 * Build a `DeepRequired` generic that turns every
 * key of a nested object structure into a required property.
 */
namespace deepRequired {
  type DeepRequired<T> = {
    [K in keyof T]-?: DeepRequired<T[K]>;
  };
 
  type res1 = DeepRequired<{ a?: string; b?: string }>;
  type test1 = Expect<Equal<res1, { a: string; b: string }>>;
 
  type res2 = DeepRequired<{ a?: { b?: string; c?: { d?: string } } }>;
  type test2 = Expect<Equal<res2, { a: { b: string; c: { d: string } } }>>;
 
  type res3 = DeepRequired<{ a?: string; b?: { c?: string; d?: number }[] }>;
  type test3 = Expect<
    Equal<res3, { a: string; b: { c: string; d: number }[] }>
  >;
}
/**
 * When managing application state, a common pattern
 * is to represent user events using "action" object.
 * Action always have a `type` discriminant property,
 * and a "payload" property containing data.
 *
 * Build a `MakeDispatchers` generic that takes a union
 * of Action objects, and turn them into an object with
 * a dispatcher method for every action. a Dispatcher takes
 * the payload and returns void.
 */
namespace dispatchers {
  type UnknownAction = { type: string; payload: unknown };
 
  // type MakeDispatchers<ActionUnion extends UnknownAction> = {
  //   [K in ActionUnion["type"]]: (payload: ActionUnion["payload"]) => void
  // }
 
  type MakeDispatchers<ActionUnion extends UnknownAction> = {
    [K in ActionUnion["type"]]: ActionUnion extends {
      type: K;
      payload: infer Payload;
    }
      ? (payload: Payload) => void
      : never;
  };
 
  type CreateAction = {
    type: "create";
    payload: { id: number; data: string[] };
  };
  type DeleteAction = { type: "delete"; payload: number };
  type PushAction = { type: "push"; payload: { id: number; data: string } };
  type PopAction = { type: "pop"; payload: number };
 
  type res1 = MakeDispatchers<CreateAction | DeleteAction>;
 
  type test1 = Expect<
    Equal<
      res1,
      {
        create: (payload: { id: number; data: string[] }) => void;
        delete: (payload: number) => void;
      }
    >
  >;
 
  type res2 = MakeDispatchers<
    CreateAction | DeleteAction | PushAction | PopAction
  >;
 
  type test2 = Expect<
    Equal<
      res2,
      {
        create: (payload: { id: number; data: string[] }) => void;
        delete: (payload: number) => void;
        push: (payload: { id: number; data: string }) => void;
        pop: (payload: number) => void;
      }
    >
  >;
}