Repository: trpc/trpc
File: packages/server/src/shared/internal/serialize.ts (TypeScript)

Code before:
 * See https://stackoverflow.com/a/49928360/1490091
 */
type IsAny<T> = 0 extends T & 1 ? true : false;

// `undefined` is a weird one that's technically not valid JSON,
// but the return value of `JSON.parse` can be `undefined` so we
// support it as both a Primitive and a NonJsonPrimitive
type JsonReturnable = JsonPrimitive | undefined;

/* prettier-ignore */
export type Serialize<T> =
  IsAny<T> extends true ? any :
  unknown extends T ? unknown :
  T extends JsonReturnable ? T :
  T extends Map<any, any> | Set<any> ? object :
  T extends NonJsonPrimitive ? never :
  T extends { toJSON(): infer U } ? U :
  T extends [] ? [] :
  T extends [unknown, ...unknown[]] ? SerializeTuple<T> :
  T extends readonly (infer U)[] ? (U extends NonJsonPrimitive ? null : Serialize<U>)[] :
  T extends object ?
      // Record<K, V> is a special case where we need to ensure that:
      // 1. we don't match narrower objects like { foo: string } that are also assignable to Record<string, string>
      // 2. the keys are not symbols
      // Excluding literal keys (e.g. "foo") from PropertyKey (string | number | symbol) does not exclude anything...
      Exclude<PropertyKey, keyof T> extends infer TheKeys extends PropertyKey ?
        PropertyKey extends TheKeys ? 
        // ...so if the remaining keys after exclusion are exactly equal to PropertyKey, we know that K is a "narrow object", i.e. not a Record.
          Simplify<SerializeObject<UndefinedToOptional<T>>> :
        // otherwise, we know that the keys are not literal i.e. this is a record with an index signature.
        // serialize it, but remove any symbol keys.
        Record<Exclude<keyof T, symbol>, Serialize<T[keyof T]>> 
    : never : never;

/** JSON serialize [tuples](https://www.typescriptlang.org/docs/handbook/2/objects.html#tuple-types) */
type SerializeTuple<T extends [unknown, ...unknown[]]> = {
  [k in keyof T]: T[k] extends NonJsonPrimitive ? null : Serialize<T[k]>;
};

/**
 * JSON serialize objects (not including arrays) and classes
 * @internal
 **/
export type SerializeObject<T extends object> = {
  [k in keyof Omit<T, FilterKeys<T, NonJsonPrimitive>>]: Serialize<T[k]>;
};

type FilterDefinedKeys<TObj extends object> = Exclude<
  {
    [TKey in keyof TObj]: undefined extends TObj[TKey] ? never : TKey;
  }[keyof TObj],

Review comment (suggestion):
Could probably use something like `type IsRecord<T> = Record<never, never> extends T ? true : false` for this to make the check simpler

Code after:
 * See https://stackoverflow.com/a/49928360/1490091
 */
type IsAny<T> = 0 extends T & 1 ? true : false;

// `undefined` is a weird one that's technically not valid JSON,
// but the return value of `JSON.parse` can be `undefined` so we
// support it as both a Primitive and a NonJsonPrimitive
type JsonReturnable = JsonPrimitive | undefined;

// prettier-ignore
export type Serialize<T> =
  IsAny<T> extends true ? any :
  unknown extends T ? unknown :
  T extends JsonReturnable ? T :
  T extends Map<any, any> | Set<any> ? object :
  T extends NonJsonPrimitive ? never :
  T extends { toJSON(): infer U } ? U :
  T extends [] ? [] :
  T extends [unknown, ...unknown[]] ? SerializeTuple<T> :
  T extends readonly (infer U)[] ? (U extends NonJsonPrimitive ? null : Serialize<U>)[] :
  T extends object ? Simplify<SerializeObject<UndefinedToOptional<T>>> :
  never;

/** JSON serialize [tuples](https://www.typescriptlang.org/docs/handbook/2/objects.html#tuple-types) */
type SerializeTuple<T extends [unknown, ...unknown[]]> = {
  [k in keyof T]: T[k] extends NonJsonPrimitive ? null : Serialize<T[k]>;
};

// prettier-ignore
type SerializeObjectKey<T extends Record<any, any>, TKey> = 
  // never include entries where the key is a symbol
  TKey extends symbol ? never : 
  // always include entries where the value is any
  IsAny<T[TKey]> extends true ? TKey :
  // always include entries where the value is unknown
  unknown extends T[TKey] ? TKey : 
  // never include entries where the value is a non-JSON primitive
  T[TKey] extends NonJsonPrimitive ? never : 
  // otherwise serialize the value
  TKey;
/**
 * JSON serialize objects (not including arrays) and classes
 * @internal
 **/
export type SerializeObject<T extends object> = {
  [$Key in keyof T as SerializeObjectKey<T, $Key>]: Serialize<T[$Key]>;
};

type FilterDefinedKeys<TObj extends object> = Exclude<
  {
    [TKey in keyof TObj]: undefined extends TObj[TKey] ? never : TKey;