Skip to content

Declaration emit proceeds in the presence of TS7056 #3094

@RyanCavanaugh

Description

@RyanCavanaugh

Steps to reproduce

types.ts

// Utility types from type-fest/remeda that create deeply nested conditional chains
type IsNever<T> = [T] extends [never] ? true : false;
type IsAny<T> = 0 extends 1 & NoInfer<T> ? true : false;
type If<T extends boolean, Then, Else> = IsNever<T> extends true ? Else : T extends true ? Then : Else;
type IfNotAnyOrNever<T, Then, IfAny = any, IfNever = never> = If<IsAny<T>, IfAny, If<IsNever<T>, IfNever, Then>>;
type Or<A extends boolean, B extends boolean> = If<IsNever<A>, false, A> extends true ? true : If<IsNever<B>, false, B> extends true ? true : false;
type CollapseLiterals<T> = {} extends T ? T : T extends infer U & {} ? U : T;
type Simplify<T> = { [K in keyof T]: T[K] } & {};

type UnionToIntersection<U> = (
  U extends unknown ? (x: U) => void : never
) extends (x: infer I) => void ? I & U : never;

type KeysOfUnion<T> = keyof UnionToIntersection<
  T extends unknown ? Record<keyof T, never> : never
>;

// Tag branding types — these create enough conditional depth to prevent type simplification
declare const tag: unique symbol;
type TagContainer<Token> = { readonly [tag]: Token };
type Tag<Token extends PropertyKey, Meta> = TagContainer<{ [K in Token]: Meta }>;
type Tagged<T, Name extends PropertyKey, Meta = never> = T & Tag<Name, Meta>;
type RemoveAllTags<T> = T extends Tag<PropertyKey, any>
  ? { [K in keyof T[typeof tag]]: T extends Tagged<infer U, K, T[typeof tag][K]> ? RemoveAllTags<U> : never }[keyof T[typeof tag]]
  : T;

// String literal detection with tag-aware unwrapping
type IsStringLiteral<S> = IfNotAnyOrNever<S,
  (CollapseLiterals<S extends TagContainer<any> ? RemoveAllTags<S> : S>) extends infer C extends string
    ? {} extends Record<C, never> ? false : true
    : false,
  false, false>;

// Recursive string splitting
type SplitHelper<S extends string, D extends string, Acc extends string[] = []> =
  S extends string ? D extends string ?
    Or<false, IsStringLiteral<S> extends true ? IsStringLiteral<D> : false> extends true
      ? S extends `${infer Head}${D}${infer Tail}` ? SplitHelper<Tail, D, [...Acc, Head]>
        : D extends '' ? S extends '' ? Acc : [...Acc, S] : [...Acc, S]
      : string[]
    : never : never;

// Bounded key detection
type IsBoundedString<T> = T extends string ? IsStringLiteral<T> extends true
  ? SplitHelper<T, "">[number] extends infer U
    ? [`${number}`] extends [U] ? false : [string] extends [U] ? false : true
    : false : false : false;

type IsBounded<T> = (T extends unknown ? Or<IsBoundedString<T>, false> : never) extends true ? true : false;

// The pick return type that triggers circular inference
type PickFromArray<T, Keys extends readonly KeysOfUnion<T>[]> =
  T extends unknown ? Keys extends unknown
    ? Simplify<IsBounded<KeysOfUnion<T>> extends true
        ? Pick<T, Extract<Keys[number], keyof T>>
        : Pick<T, Extract<Keys[number], keyof T>>>
    : never : never;

export declare function pick<T extends object, const Keys extends readonly KeysOfUnion<T>[]>(
  data: T, keys: Keys
): PickFromArray<T, Keys>;

repro.ts

import { pick } from "./types";

export class MyClass {
  constructor(public readonly field: number) {}
  getIdentity() {
    return pick(this, ["field"]);
  }
}

Run tsc -d / tsgo -d

Behavior with typescript@6.0

epro.ts:5:3 - error TS2527: The inferred type of 'getIdentity' references an inaccessible 'unique symbol' type. A type annotation is necessary.

5   getIdentity() {
    ~~~~~~~~~~~

repro.ts:5:3 - error TS4053: Return type of public method from exported class has or is using name 'tag' from external module "D:/Throwaway/63207_/types" but cannot be named.

5   getIdentity() {
    ~~~~~~~~~~~

repro.ts:5:3 - error TS7056: The inferred type of this node exceeds the maximum length the compiler will serialize. An explicit type annotation is needed.

5   getIdentity() {
    ~~~~~~~~~~~


Found 3 errors in the same file, starting at: repro.ts:5

Behavior with tsgo

Same errors, but emits a 2.4 MB repro.d.ts file

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions