/**
* 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;
}
>
>;
}