TypeScript icon indicating copy to clipboard operation
TypeScript copied to clipboard

Incomprehensible incompatibility around Partial, Conditional types and Generics.

Open lorefnon opened this issue 5 years ago • 4 comments

TypeScript Version: 3.9.0-dev.20200322

Search Terms: partial, conditional types

Code

type SafePartial<T> = T extends {} ? Partial<T> : any;

interface QB<TRecord extends {}> {
  insert(record: SafePartial<TRecord>): void;
}


async function insert1<TRecord extends {}>(qb: QB<TRecord>, record: TRecord) {
  await qb.insert(record);
}

Expected behavior:

The code successfully compiles

Actual behavior:

Following error is observed for qb.insert(record):

Argument of type 'TRecord' is not assignable to parameter of type 'SafePartial<TRecord>'. Type '{}' is not assignable to type 'SafePartial<TRecord>'.

The issue goes away if I use Partial instead of the SafePartial above.

The issue also goes away if a concrete type is used instead of generic type:

interface User {
    id: string;
}

async function insert1(qb: QB<User>, record: User) {
  await qb.insert(record);
}

Playground Link: Playground

lorefnon avatar Mar 23 '20 05:03 lorefnon

Similar/same issue as open bug for ts-essentials library:

export type Test<T> = T extends number // doesn't really matter what the condition is
  ? Partial<T>
  : Partial<T>; 

function conditionalPartialTestErrors<T>(): Test<T> {
  return {}; // error
}

function conditionalPartialTestCompiles<T>(): Partial<T> | Test<T> {
  return {}; // no error
}

bbarry avatar Sep 25 '20 12:09 bbarry

I think it works if we remove Distribution from the equation and hence it will "resolve" sooner rather than at the instantiation step.

type Test<T> = [T] extends number? Partial<T> : Partial<T>
function conditionalPartialTestErrors<T>(){
  return (state: Test<T> = {}) => {
  }
}
function conditionalPartialTestCompiles<T>(){
  return (state: Test<T> = {}) => {
  }
}

Not sure though why one would "resolve" sooner and the other one would have to be deferred till instantiation.

ajitjha393 avatar Nov 07 '22 11:11 ajitjha393

I found a repro without distribution:

type C<A> = [A] extends [{}] ? { a: string } : { a: number };
<A extends {}>(): C<A> => ({ a: 'x' });

// Error: Type '{ a: string; }' is not assignable to type 'C<A>'.(2322)

Playground

seems Partial is unrelated

ikeyan avatar Aug 25 '24 09:08 ikeyan

The compiler cannot determine assignability for conditional types when either T or U is not fully defined, preventing it from resolving T extends U ? V : W.

Here's a great example explaining this in detail by jcalz https://stackoverflow.com/a/56014202/10310568

ajitjha393 avatar Aug 28 '24 16:08 ajitjha393