#`T extends true`? What is even a type in TS?

21 messages · Page 1 of 1 (latest)

knotty rapids
#

Looking at an example from the TS handbook (not latest version): https://www.typescriptlang.org/docs/handbook/advanced-types.html#conditional-types

declare function f<T extends boolean>(x: T): T extends true ? string : number;

I couldn't find any documentation or other reference for a conditional type like T extends true ?... .
I sorta kinda understand that string unions can be a type and so in particular a single string can serve as a type, but true/false?? Even worse, it works for other literals (eg numeric):

declare function g<T extends number>(x: T): T extends 1 ? string : number;

Can any value really function as a type in conditional types? If so, what is even a type in TS?

sleek heraldBOT
#
ofekshilon#0

Preview:```ts
declare function f<T extends boolean>(
x: T
): T extends true ? string : number

// Type is 'string | number'
let x = f(Math.random() < 0.5)
// ^?

declare function g<T extends number>(
x: T
): T extends 1 ? string : number```

dusky gazelle
#

@knotty rapids Yes, literal primitives values are types in TS.

#

true and false are used pretty commonly:

type Options = {
   multi: true,
   value: string[]
} | {
   multi: false,
   value: string
}
#

And strings are commonly used like you said, for unions:

type Color = "red" | "blue" | "green"; // like enum but better
#

Numeric literals are, I think, a bit less common, but you can use them too.

knotty rapids
#

@dusky gazelle thank you. So what does it mean to 'extend' true? (or for that matter, to 'extend' boolean?)
Is this term used just for lack of syntax like 'is true'/'is boolean'?

dusky gazelle
#

Yeah T extends true is realistically just a way of checking that T is exactly equal to true.

silent rivet
#

A extends B is more like "A is assignable to B"

dusky gazelle
#

But extends boolean is 'meaningful' because true and false are both subtypes of boolean. Basically in TS type boolean = true | false

#

And yeah, thinking of "extends" as "is assignable to" or "is subtype of" is useful - it doesn't necessarily mean class inheritance (though that is one form of subtyping)

knotty rapids
#

!resolved

knotty rapids
dusky gazelle
#

Not really? If B is 14 then A is assignable to B only when A is 14.

#
type B = 14;

// As values:
const b: B = 14; // 14 is assignable to B 
const fail: B = 15; // 15 is not assignable to B

// As types
type Test<A> = A extends B ? "Yes" : "No";
type Yes = Test<14>; // 14 is assignable to 14
type No = Test<15>; // 15 is not assignable to 14
knotty rapids
silent rivet
#

TS type system closely relates to set theory, a type is a set that contains all values that is assignable to it. For example, the type number is a set that contains all possible numbers, and similarly the type 14 is a set that contains only that single specific number. When you test A extends B, you are testing "if A is a subset of B"

silent rivet
# knotty rapids 'Assignable to 14' seems to imply that the literal 14 can be assigned to. That'...

"Literal 14 can be assigned to"
That's not really what's happening here. const a: 14 = 14, isn't "assigning 14 to 14" it's "assigning value 14 to variable a." Literal 14 isn't being assigned to, the variable a is. And in order to know if "assigning a value to a variable" is legal, the type checker needs to check if the type of the value is assignable to the type of the variable, so it tests 14 extends 14, and both 14s here are not values, they are types.
The code A extends B is really testing:

let a: A = ...
let b: B = ...
// is it legal to do:
a = b

So 14 extends 14 isn't testing "is it legal to do 14 = 14" it's still testing "is it legal to do a = b"

#

A lot of languages don't have literal types, they only have types like string that represent many values, so it might be a hard concept to grasp. But one way to think about it is that, const a: number = ... is saying "it's possible for a to have a value of any number" and const a: 42 | 69 = ... is saying "it's possible for a to have a value of either one of those two numbers, it can't be any other number" and const a: 14 = ... is an even more extreme version of it where "a must have a value of 14 and nothing else"

#

If const a: 42 | 69 = ... seems weird to you, remember that const a: boolean = ... is the same as const a: true | false = ....