2

Here's my code and the related playground:

type SomeType<T> = {
  [K in keyof T]?: K extends 'u'
    ? string
    : T[K] extends object
      ? SomeType<T[K]>
      : object
}

function someType<T>(t: SomeType<T>) {
  return t;
}

type BLA = SomeType<{
  u: 1,
  b: {
    u: 1,
  },
}>;

const blaBLA: BLA = {
  u: 1, // <-- correct error msg: Type 'number' is not assignable to type 'string'.
  b: {
    u: 1, // <-- correct error msg: Type 'number' is not assignable to type 'string'.
  },
};

const bla = someType({
  u: 1, // <-- correct error msg: Type 'number' is not assignable to type 'string'.
  b: {
    u: 1, // <-- expected: error message from above
          // the problem here is that the value for b was resolved to 'object' instead of 'SomeType<T[K]>'
          // how can I fix this?
  },
});

I am either looking for a fix to the problem which I describe in the code above or an explaination why it doesn't work the way I think it works.

As this is purely for learning (understanding) TS, I would be okay with either outcome.

5
  • 1
    If you have a function of the form func<T>(x: F<T>):void and call f(x) then the compiler is going to take x as type F<T> and try to infer T from it. If F<T> is complicated enough, this is going to fail. Instead it looks like you really want func<T extends F<T>>(x: T): void, where T is easily inferred from x, and then just checked against F<T>, like this. Does that meet your needs (and I could write up an answer) or am I missing something? Commented Jun 12, 2022 at 2:08
  • I just made a comment that your solution doesn't work for arbitrary levels. (This statement was wrong and removed) You're a f genius. Thank you also for the complexity explaination. This absolutely makes sense! (yes, please make an answer out of your comment) <3 Commented Jun 12, 2022 at 10:51
  • I noticed autocompletion is not working for someType({ ... }). This is just a side note. I may ask a seperate question and link it here but I am still "investigating" with my limited skills. Commented Jun 12, 2022 at 13:04
  • Fixed it myself. (Playground) I feel like I outsmarted myself. Commented Jun 12, 2022 at 14:14
  • Note: I just found the autocomplete solution to not work, when the input to someType(...) is correct. See question. Commented Jun 12, 2022 at 16:22

1 Answer 1

2
+50

If you have a function like

function someType<T>(t: SomeType<T>) {
  return t;
}

and call it with someType(val), then the compiler is going to try to infer the generic type parameter T given that val is of the type SomeType<T>. You're asking the compiler to essentially invert the type function SomeType<T>.

But that's not really possible for the compiler to do, especially because since SomeType<T> might be the same type as SomeType<U> even when T and U are different:

type Hmm = SomeType<{ u: 1, b: "", c: { d: "" } }>;
/* type Hmm = {
    u?: string;
    b?: object;
    c?: {d?: object}
} */

type Ugh = SomeType<{ u: true, b: 123, c: { d: false } }>;
/* type Ugh = {
    u?: string;
    b?: object;
    c?: {d?: object}
} */

Inverting type functions isn't always possible, or even when it is possible, it's not always feasible for the compiler to do it.

Luckily, this isn't really the behavior you want.


Rather, it looks like you would like SomeType<T> to act as a recursive constraint of T itself. What I mean is that when you call someType(val), you want T to be inferred as the type of val, and then you want the compiler to check that T is also assignable to SomeType<T>.

And so let's just write that directly as a generic constraint:

function someType<T extends SomeType<T>>(t: T) {
  return t;
}

That compiles with no error, so we can proceed to test it:

const bla = someType({
  u: 1, // error!
  b: {
    u: 1, // error!
  },
});

Looks good. You get exactly the errors you expect to get.

Playground link to code

Sign up to request clarification or add additional context in comments.

1 Comment

Perfect! Now I have "only" 1 more problem to solve. (Autocompletion within someType({...});) but I am not sure that this is possible after your explaination because it means I cannot really change the signature (t: T). ^.-

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.