0

Why does this unknown definitely not extend any[]?

In the code below any returns boolean as it could be either, however unknown doesn't do the same, why is that?

type IsAnyArray1 = unknown extends any[] ? true : false // false
type IsAnyArray2 = any extends any[] ? true : false // boolean
TrevTheDev
  • 2,616
  • 2
  • 18
  • 36
  • I would put the question back on you. Why do you think `unknown` should extend `any[]`? Why do you think every `unknown` value should be able to be automatically treated as an array? – Silvio Mayolo Mar 07 '22 at 04:13
  • The real question here shouldn't be "why doesn't `unknown` extend `any[]`", because you cannot assign a value of type `unknown` to a variable of type `any[]`; `unknown` is absolutely not a subtype of `any[]`. On the other hand, `any` is considered to be (unsoundly) both a subtype and supertype of every type, so `any extends any[]` is always true (you can always assign a value of type `any` to a variable of type `any[]`). No, the real question is: why does `IsAnyArray2` return `boolean` instead of `true`? If you understand and agree, I can look at answering that. Let me know. – jcalz Mar 07 '22 at 16:05
  • @Silvio Mayolo - I don't think it SHOULD extend `any[]` only that it MAY extend `any[]` - i.e. it should be the same as `any` which could be any value and so returns boolean. Unknown leaves me confused and makes my head hurt! – TrevTheDev Mar 08 '22 at 02:46
  • @jcalz - that's the most understandable response I've seen to this question. These things would make more sense to me if as you pointed out `any extends any[]` always returned `true` not `boolean` – TrevTheDev Mar 08 '22 at 02:53
  • `any` is intentionally an unsafe mess of a type. That's the point of it. Trying to apply good programming practices or actual type theory to it will fail, for the simple reason that it is, and has always been, a backdoor to *fight* the type checker, and that's by design. – Silvio Mayolo Mar 08 '22 at 03:15
  • The question "why does `any extends XXX ? true : false` return `boolean` instead of `true`" was asked [here](https://stackoverflow.com/q/68754652/2887218); do you agree that this question is essentially a duplicate of that one (since the inconsistency here is with `any` and not `unknown`)? Or am I missing something? – jcalz Mar 08 '22 at 03:27
  • @jcalz - Most people understand `unknown` to be "the type-safe counterpart of any" - so one would reasonably expect `unknown` and `any` to behave in similar manners, however as this question shows they don't. Likely what's required is an authoritative answer clarifying the reasons for this divergence. – TrevTheDev Mar 08 '22 at 23:34
  • Still seems like a duplicate of the other question which also tests `any` vs `unknown`. But I would expect the "type safe counterpart of `any`" to behave *differently* from `any` precisely because of the difference in type safety. Are you saying you want an answer that explains all the ways in which `any` and `unknown` are the same or different? That would be quite a long answer and isn't directly asked for in the question. The only deviation here is with `any`. `(any[] | string) extends any[] ? true : false` is `false`. `object extends any[] ? true : false` is `false`. – jcalz Mar 09 '22 at 01:54
  • So either this question is really about why `any extends any[] ? true : false` is `boolean` instead of `false`, in which case it's a duplicate... or this question is really about why `unknown extends any[] ? true : false` is `false`, in which case `any` is a red herring because it's the only type that behaves differently. I can write up an answer to that, but the existing answer here is essentially answering the former (without explaining why `any` is a red herring here). I'm still open to being persuaded otherwise, but so far I'm leaning toward closing this. – jcalz Mar 09 '22 at 01:57

1 Answers1

2

The main issue here is your question itself. When you say that A extends B ? true : false, it will be true if and only if A is assignable to B and false otherwise. Now, let's now look at your question again:

Why does this unknown definitely not extend any[]?

unknown is a type that is explicitly named for forcing you as a developer to not take any guarantees on the shape of the data. Therefore, you cannot assign unknown to anything except any and unknown. You are instead supposed to get the shape of the data by inferring it via checks (if statements like typeof foo === "string") or direct casts (foo as boolean).

Therefore, when you ask why does unknown not extend any[] you are asking why is unknown not assignable to any[]. The reason for this is because when you say that unknown is assignable to any[] then you are making an unsafe guarantee that the data is in fact an array. The fact that the item within the array is any does not matter because you are still saying that the data should have all of the methods and index signatures of the array type. I hope this helps clear things up for you/

sno2
  • 3,274
  • 11
  • 37
  • Thanks for the answer, unfortunately, I remain confused: `unknown` can be any type, and one has to determine its type before using or casting it as you point out. However, an `unknown` type could in fact be of type `any[]` - so how come the compiler is so sure it is definitely not of type `any[]` - the way it handles `any` seems more consistent. I.e. an unknown type could, or could not be of type any[] - which is the way `any` handles it. – TrevTheDev Mar 07 '22 at 07:16
  • @TrevTheDev `unknown` could be an `any[]`, but that does not matter because `unknown` is only assignable to itself and `any`. Also, note that you are using constant values for the left hand side of the conditional type clause. If it was a generic that extended `unknown`, then you are correct, it may be `true` or `false`. However, you passed in the `unknown` type so it must always be `false` unless explicitly casted into a compatible type. – sno2 Mar 07 '22 at 13:15