Repository: motion-canvas/motion-canvas
File: packages/core/src/tweening/interpolationFunctions.ts (TypeScript)

Code before:
 *
 * @param from - The input to favor when value is 0.
 * @param to - The input to favor when value is 1.
 * @param value - On a scale between 0 and 1, how closely to favor from vs to.
 *
 * @returns A value matching the structure of from and to.
 */
export function deepLerp<
  TFrom extends Record<any, unknown>,
  TTo extends Record<any, unknown>,
>(from: TFrom, to: TTo, value: number): TFrom | TTo;
/**
 * Interpolate between any two values, including objects, arrays, and Maps.
 *
 * @param from - The input to favor when value is 0.
 * @param to - The input to favor when value is 1.
 * @param value - On a scale between 0 and 1, how closely to favor from vs to.
 *
 * @returns A value matching the structure of from and to.
 */
export function deepLerp<T>(from: T, to: T, value: number): T;
export function deepLerp(from: any, to: any, value: number): any {
  if (value === 0) return from;
  if (value === 1) return to;

  if (typeof from === 'undefined' || typeof to === 'undefined') {
    return undefined;
  }

  if (typeof from === 'number' && typeof to === 'number') {
    return map(from, to, value);
  }

  if (typeof from === 'string' && typeof to === 'string') {
    return textLerp(from, to, value);
  }

  if ('lerp' in from) {
    return from.lerp(to, value);
  }

  if (from && to && typeof from === 'object' && typeof to === 'object') {
    if (Array.isArray(from) && Array.isArray(to)) {
      if (from.length === to.length) {
        return from.map((f, i) => deepLerp(f, to[i], value));
      }
    } else {
      let toObject = false;
      if (!(from instanceof Map) && !(to instanceof Map)) {
        toObject = true;
        from = new Map(Object.entries(from));

Review comment (suggestion):
This was letting `null` slip through as well, which caused issues further down the function.

Code after:
 * 1.
 *
 * @param from - The input to favor when value is 0.
 * @param to - The input to favor when value is 1.
 * @param value - On a scale between 0 and 1, how closely to favor from vs to.
 *
 * @returns A value matching the structure of from and to.
 */
export function deepLerp<
  TFrom extends Record<any, unknown>,
  TTo extends Record<any, unknown>,
>(from: TFrom, to: TTo, value: number): TFrom | TTo;
export function deepLerp<
  TFrom extends Record<any, unknown>,
  TTo extends Record<any, unknown>,
>(from: TFrom, to: TTo, value: number, suppressWarnings: boolean): TFrom | TTo;
/**
 * Interpolate between any two values, including objects, arrays, and Maps.
 *
 * @param from - The input to favor when value is 0.
 * @param to - The input to favor when value is 1.
 * @param value - On a scale between 0 and 1, how closely to favor from vs to.
 *
 * @returns A value matching the structure of from and to.
 */
export function deepLerp<T>(from: T, to: T, value: number): T;
export function deepLerp<T>(
  from: T,
  to: T,
  value: number,
  suppressWarnings: boolean,
): T;
export function deepLerp(
  from: any,
  to: any,
  value: number,
  suppressWarnings = false,
): any {
  if (value === 0) return from;
  if (value === 1) return to;

  if (from == undefined || to == undefined) {
    if (!suppressWarnings) {
      useLogger().warn(
        `Attempting to lerp ${from} -> ${to} may result in unexpected behavior.`,
      );
    }
    return undefined;
  }

  if (typeof from === 'number' && typeof to === 'number') {