Skip to content

Fixes for tests #2

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Mar 12, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 36 additions & 20 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1403,20 +1403,31 @@ const enum IntrinsicTypeKind {
Sub,
Mul,
Div,
Integer,
Floor,
Ceil,
Round,
}

// Set.has is faster than Array.includes
const intrinsicUnaryKinds = new Set([
IntrinsicTypeKind.Floor,
IntrinsicTypeKind.Ceil,
IntrinsicTypeKind.Round,
]);

const intrinsicTypeKinds: ReadonlyMap<string, IntrinsicTypeKind> = new Map(Object.entries({
Uppercase: IntrinsicTypeKind.Uppercase,
Lowercase: IntrinsicTypeKind.Lowercase,
Capitalize: IntrinsicTypeKind.Capitalize,
Uncapitalize: IntrinsicTypeKind.Uncapitalize,
NoInfer: IntrinsicTypeKind.NoInfer,
Add: IntrinsicTypeKind.Add,
Sub: IntrinsicTypeKind.Sub,
Mul: IntrinsicTypeKind.Mul,
Div: IntrinsicTypeKind.Div,
Integer: IntrinsicTypeKind.Integer,
Subtract: IntrinsicTypeKind.Sub,
Multiply: IntrinsicTypeKind.Mul,
Divide: IntrinsicTypeKind.Div,
Floor: IntrinsicTypeKind.Floor,
Ceil: IntrinsicTypeKind.Ceil,
Round: IntrinsicTypeKind.Round,
}));

const SymbolLinks = class implements SymbolLinks {
Expand Down Expand Up @@ -18387,12 +18398,14 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
function getIntrinsicMappingType(symbol: Symbol, type: Type): Type {
return type.flags & (TypeFlags.Union | TypeFlags.Never) ? mapType(type, t => getIntrinsicMappingType(symbol, t)) :
type.flags & TypeFlags.StringLiteral ? getStringLiteralType(applyStringMapping(symbol, (type as StringLiteralType).value)) :
type.flags & TypeFlags.NumberLiteral ? applyNumberMapping(symbol, type as NumberLiteralType) :
type.flags & TypeFlags.NumberLiteral ? getNumberLiteralType(applyNumericUnary(symbol, (type as NumberLiteralType).value)) :
type.flags & TypeFlags.TemplateLiteral ? getTemplateLiteralType(...applyTemplateStringMapping(symbol, (type as TemplateLiteralType).texts, (type as TemplateLiteralType).types)) :
// Mapping<Mapping<T>> === Mapping<T>
type.flags & TypeFlags.StringMapping && symbol === type.symbol ? type :
type.flags & (TypeFlags.Any | TypeFlags.String | TypeFlags.StringMapping) || isGenericIndexType(type) ? (getBaseConstraintOfType(type)?.flags ?? 0) & TypeFlags.StringLike ? getStringMappingTypeForGenericType(symbol, type) :
getCalculationTypeForGenericType(symbol, [type]) :
type.flags & TypeFlags.Calculation ? getCalculationTypeForGenericType(symbol, [type]) :
// Floor<T>/Ceil<T>/Round<T> === T when T is any, number, or bigint
(intrinsicUnaryKinds.has(intrinsicTypeKinds.get(symbol.escapedName as string)!) && (type.flags & (TypeFlags.Any | TypeFlags.Number | TypeFlags.BigInt))) ? type :
type.flags & (TypeFlags.Any | TypeFlags.String | TypeFlags.StringMapping) || isGenericIndexType(type) ? getStringMappingTypeForGenericType(symbol, type) :
// This handles Mapping<`${number}`> and Mapping<`${bigint}`>
isPatternLiteralPlaceholderType(type) ? getStringMappingTypeForGenericType(symbol, getTemplateLiteralType(["", ""], [type])) :
type;
Expand All @@ -18406,29 +18419,33 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
// handle division by zero
intrinsicTypeKinds.get(symbol.escapedName as string) === IntrinsicTypeKind.Div && type2.flags & TypeFlags.NumberLiteral && (type2 as NumberLiteralType).value === 0 ? neverType :
type1.flags & TypeFlags.NumberLiteral ?
type2.flags & TypeFlags.NumberLiteral ? applyNumberMapping2(symbol, type1 as NumberLiteralType, type2 as NumberLiteralType) :
type2.flags & TypeFlags.NumberLiteral ? getNumberLiteralType(applyNumericBinary(symbol, (type1 as NumberLiteralType).value, (type2 as NumberLiteralType).value)) :
type2 :
type1;
}

function applyNumberMapping(symbol: Symbol, n: NumberLiteralType): Type {
function applyNumericUnary(symbol: Symbol, value: number): number {
switch (intrinsicTypeKinds.get(symbol.escapedName as string)) {
case IntrinsicTypeKind.Integer:
return getNumberLiteralType(Math.floor(n.value));
case IntrinsicTypeKind.Floor:
return Math.floor(value);
case IntrinsicTypeKind.Ceil:
return Math.ceil(value);
case IntrinsicTypeKind.Round:
return Math.round(value);
}
return n;
return value;
}

function applyNumberMapping2(symbol: Symbol, a: NumberLiteralType, b: NumberLiteralType): Type {
function applyNumericBinary(symbol: Symbol, a: number, b: number): number {
switch (intrinsicTypeKinds.get(symbol.escapedName as string)) {
case IntrinsicTypeKind.Add:
return getNumberLiteralType(a.value + b.value);
return (a + b);
case IntrinsicTypeKind.Sub:
return getNumberLiteralType(a.value - b.value);
return (a - b);
case IntrinsicTypeKind.Mul:
return getNumberLiteralType(a.value * b.value);
return (a * b);
case IntrinsicTypeKind.Div:
return getNumberLiteralType(a.value / b.value);
return (a / b);
}
return a;
}
Expand Down Expand Up @@ -18804,8 +18821,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
}

function isGenericStringLikeType(type: Type) {
// @todo TypeFlags.Calculation not exactly stringlike right?
return !!(type.flags & (TypeFlags.TemplateLiteral | TypeFlags.StringMapping | TypeFlags.Calculation)) && !isPatternLiteralType(type);
return !!(type.flags & (TypeFlags.TemplateLiteral | TypeFlags.StringMapping)) && !isPatternLiteralType(type);
}

function isGenericType(type: Type): boolean {
Expand Down
4 changes: 3 additions & 1 deletion src/harness/fourslashInterfaceImpl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1209,7 +1209,9 @@ export namespace Completion {
typeEntry("Lowercase"),
typeEntry("Capitalize"),
typeEntry("Uncapitalize"),
typeEntry("Integer"),
typeEntry("Floor"),
typeEntry("Ceil"),
typeEntry("Round"),
typeEntry("Add"),
typeEntry("Subtract"),
typeEntry("Multiply"),
Expand Down
14 changes: 12 additions & 2 deletions src/lib/es5.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1650,9 +1650,19 @@ type Capitalize<S extends string> = intrinsic;
type Uncapitalize<S extends string> = intrinsic;

/**
* Convert number literal type to integer
* Convert a number literal type to an integer by flooring it
*/
type Integer<N extends number> = intrinsic;
type Floor<N extends number> = intrinsic;

/**
* Convert a number literal type to an integer by ceiling it
*/
type Ceil<N extends number> = intrinsic;

/**
* Convert a number literal type to integer by rounding it
*/
type Round<N extends number> = intrinsic;

/**
* Add two literal numbers
Expand Down
20 changes: 15 additions & 5 deletions tests/baselines/reference/api/typescript.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6549,13 +6549,14 @@ declare namespace ts {
NonPrimitive = 67108864,
TemplateLiteral = 134217728,
StringMapping = 268435456,
Calculation = -2147483648,
Literal = 2944,
Unit = 109472,
Freshable = 2976,
StringOrNumberLiteral = 384,
PossiblyFalsy = 117724,
StringLike = 402653316,
NumberLike = 296,
NumberLike = -2147483352,
BigIntLike = 2112,
BooleanLike = 528,
EnumLike = 1056,
Expand All @@ -6565,10 +6566,10 @@ declare namespace ts {
StructuredType = 3670016,
TypeVariable = 8650752,
InstantiableNonPrimitive = 58982400,
InstantiablePrimitive = 406847488,
Instantiable = 465829888,
StructuredOrInstantiable = 469499904,
Narrowable = 536624127,
InstantiablePrimitive = -1740636160,
Instantiable = -1681653760,
StructuredOrInstantiable = -1677983744,
Narrowable = -1610859521,
}
type DestructuringPattern = BindingPattern | ObjectLiteralExpression | ArrayLiteralExpression;
interface Type {
Expand Down Expand Up @@ -6764,6 +6765,15 @@ declare namespace ts {
symbol: Symbol;
type: Type;
}
interface CalculationType extends InstantiableType {
symbol: Symbol;
types: [
Type,
] | [
Type,
Type,
];
}
interface SubstitutionType extends InstantiableType {
objectFlags: ObjectFlags;
baseType: Type;
Expand Down
164 changes: 164 additions & 0 deletions tests/baselines/reference/intrinsicTypes.errors.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,28 @@ intrinsicTypes.ts(42,5): error TS2322: Type 'string' is not assignable to type '
intrinsicTypes.ts(43,5): error TS2322: Type 'Uppercase<T>' is not assignable to type 'Uppercase<U>'.
Type 'T' is not assignable to type 'U'.
'T' is assignable to the constraint of type 'U', but 'U' could be instantiated with a different subtype of constraint 'string'.
intrinsicTypes.ts(61,18): error TS2344: Type 'string' does not satisfy the constraint 'number'.
intrinsicTypes.ts(76,17): error TS2344: Type 'string' does not satisfy the constraint 'number'.
intrinsicTypes.ts(77,20): error TS2344: Type 'string' does not satisfy the constraint 'number'.
intrinsicTypes.ts(78,18): error TS2344: Type 'string' does not satisfy the constraint 'number'.
intrinsicTypes.ts(93,22): error TS2344: Type 'string' does not satisfy the constraint 'number'.
intrinsicTypes.ts(94,25): error TS2344: Type 'string' does not satisfy the constraint 'number'.
intrinsicTypes.ts(95,23): error TS2344: Type 'string' does not satisfy the constraint 'number'.
intrinsicTypes.ts(110,22): error TS2344: Type 'string' does not satisfy the constraint 'number'.
intrinsicTypes.ts(111,25): error TS2344: Type 'string' does not satisfy the constraint 'number'.
intrinsicTypes.ts(112,23): error TS2344: Type 'string' does not satisfy the constraint 'number'.
intrinsicTypes.ts(127,20): error TS2344: Type 'string' does not satisfy the constraint 'number'.
intrinsicTypes.ts(128,23): error TS2344: Type 'string' does not satisfy the constraint 'number'.
intrinsicTypes.ts(129,21): error TS2344: Type 'string' does not satisfy the constraint 'number'.
intrinsicTypes.ts(147,5): error TS2322: Type 'number' is not assignable to type 'Add<T, U>'.
intrinsicTypes.ts(148,5): error TS2322: Type 'Multiply<T, U>' is not assignable to type 'Add<T, U>'.
Type 'number' is not assignable to type 'Add<T, U>'.
intrinsicTypes.ts(149,5): error TS2322: Type 'number' is not assignable to type 'Multiply<T, U>'.
intrinsicTypes.ts(150,5): error TS2322: Type 'Add<T, U>' is not assignable to type 'Multiply<T, U>'.
Type 'number' is not assignable to type 'Multiply<T, U>'.


==== intrinsicTypes.ts (25 errors) ====
type TU1 = Uppercase<'hello'>; // "HELLO"
type TU2 = Uppercase<'foo' | 'bar'>; // "FOO" | "BAR"
type TU3 = Uppercase<string>; // Uppercase<string>
Expand Down Expand Up @@ -81,4 +102,147 @@ intrinsicTypes.ts(43,5): error TS2322: Type 'Uppercase<T>' is not assignable to
function foo4<U extends string>(x: Uppercase<U>) {
return foo3(x);
}

type TI1 = Floor<3.5>; // 3
type TI2 = Floor<2.5 | 3.4>; // 2 | 3
type TI3 = Floor<number>; // number
type TI4 = Floor<any>; // any
type TI5 = Floor<never>; // never
type TI6 = Floor<'42'>; // Error
~~~~
!!! error TS2344: Type 'string' does not satisfy the constraint 'number'.

type TA1 = Add<4, 2>; // 6
type TA2L = Add<4 | 5, 2>; // 6 | 7
type TA2R = Add<4, 2 | 3>; // 6 | 7
type TA2LR = Add<4 | 5, 2 | 3>; // 6 | 7 | 8
type TA3L = Add<number, 2>; // number
type TA3R = Add<4, number>; // number
type TA3LR = Add<number, number>; // number
type TA4L = Add<any, 2>; // any
type TA4R = Add<4, any>; // any
type TA4LR = Add<any, any>; // any
type TA5L = Add<never, 2>; // never
type TA5R = Add<4, never>; // never
type TA5LR = Add<never, never>; // never
type TA6L = Add<'4', 2>; // Error
~~~
!!! error TS2344: Type 'string' does not satisfy the constraint 'number'.
type TA6R = Add<4, '2'>; // Error
~~~
!!! error TS2344: Type 'string' does not satisfy the constraint 'number'.
type TA6LR = Add<'4', '2'>; // Error
~~~
!!! error TS2344: Type 'string' does not satisfy the constraint 'number'.

type TS1 = Subtract<4, 2>; // 2
type TS2L = Subtract<4 | 5, 2>; // 2 | 3
type TS2R = Subtract<4, 2 | 3>; // 2 | 1
type TS2LR = Subtract<4 | 5, 2 | 3>; // 2 | 1 | 3
type TS3L = Subtract<number, 2>; // number
type TS3R = Subtract<4, number>; // number
type TS3LR = Subtract<number, number>; // number
type TS4L = Subtract<any, 2>; // any
type TS4R = Subtract<4, any>; // any
type TS4LR = Subtract<any, any>; // any
type TS5L = Subtract<never, 2>; // never
type TS5R = Subtract<4, never>; // never
type TS5LR = Subtract<never, never>; // never
type TS6L = Subtract<'4', 2>; // Error
~~~
!!! error TS2344: Type 'string' does not satisfy the constraint 'number'.
type TS6R = Subtract<4, '2'>; // Error
~~~
!!! error TS2344: Type 'string' does not satisfy the constraint 'number'.
type TS6LR = Subtract<'4', '2'>; // Error
~~~
!!! error TS2344: Type 'string' does not satisfy the constraint 'number'.

type TM1 = Multiply<4, 2>; // 8
type TM2L = Multiply<4 | 5, 2>; // 8 | 10
type TM2R = Multiply<4, 2 | 3>; // 8 | 12
type TM2LR = Multiply<4 | 5, 2 | 3>; // 8 | 12 | 10 | 15
type TM3L = Multiply<number, 2>; // number
type TM3R = Multiply<4, number>; // number
type TM3LR = Multiply<number, number>; // number
type TM4L = Multiply<any, 2>; // any
type TM4R = Multiply<4, any>; // any
type TM4LR = Multiply<any, any>; // any
type TM5L = Multiply<never, 2>; // never
type TM5R = Multiply<4, never>; // never
type TM5LR = Multiply<never, never>; // never
type TM6L = Multiply<'4', 2>; // Error
~~~
!!! error TS2344: Type 'string' does not satisfy the constraint 'number'.
type TM6R = Multiply<4, '2'>; // Error
~~~
!!! error TS2344: Type 'string' does not satisfy the constraint 'number'.
type TM6LR = Multiply<'4', '2'>; // Error
~~~
!!! error TS2344: Type 'string' does not satisfy the constraint 'number'.

type TD1 = Divide<4, 2>; // 2
type TD2L = Divide<4 | 5, 2>; // 2 | 2.5
type TD2R = Divide<4, 2 | 4>; // 2 | 1
type TD2LR = Divide<4 | 5, 2 | 4>; // 2 | 1 | 2.5 | 1.25
type TD3L = Divide<number, 2>; // number
type TD3R = Divide<4, number>; // number
type TD3LR = Divide<number, number>; // number
type TD4L = Divide<any, 2>; // any
type TD4R = Divide<4, any>; // any
type TD4LR = Divide<any, any>; // any
type TD5L = Divide<never, 2>; // never
type TD5R = Divide<4, never>; // never
type TD5LR = Divide<never, never>; // never
type TD6L = Divide<'4', 2>; // Error
~~~
!!! error TS2344: Type 'string' does not satisfy the constraint 'number'.
type TD6R = Divide<4, '2'>; // Error
~~~
!!! error TS2344: Type 'string' does not satisfy the constraint 'number'.
type TD6LR = Divide<'4', '2'>; // Error
~~~
!!! error TS2344: Type 'string' does not satisfy the constraint 'number'.
type TD7 = Divide<1, 0>; // never

type TIX1<S extends number> = Floor<S>;
type TIX2 = TIX1<4.2>; // 4
type TAX1<M extends number, N extends number> = Add<M, N>;
type TAX2 = TAX1<4, 2>; // 6
type TSX1<M extends number, N extends number> = Subtract<M, N>;
type TSX2 = TSX1<4, 2>; // 6
type TMX1<M extends number, N extends number> = Multiply<M, N>;
type TMX2 = TMX1<4, 2>; // 8
type TDX1<M extends number, N extends number> = Divide<M, N>;
type TDX2 = TDX1<4, 2>; // 2
type TAMX = Add<2, Multiply<5, 8>> // 42

function foo5<T extends number, U extends T>(s: number, x: Add<T, U>, y: Multiply<T, U>) {
s = x;
s = y;
x = s; // Error
~
!!! error TS2322: Type 'number' is not assignable to type 'Add<T, U>'.
x = y; // Error
~
!!! error TS2322: Type 'Multiply<T, U>' is not assignable to type 'Add<T, U>'.
!!! error TS2322: Type 'number' is not assignable to type 'Add<T, U>'.
y = s; // Error
~
!!! error TS2322: Type 'number' is not assignable to type 'Multiply<T, U>'.
y = x; // Error
~
!!! error TS2322: Type 'Add<T, U>' is not assignable to type 'Multiply<T, U>'.
!!! error TS2322: Type 'number' is not assignable to type 'Multiply<T, U>'.
}

function foo6<T extends 0 | 1>(x: Add<T, 3>) {
let s: 3 | 4 = x;
}

declare function foo7<T extends number>(x: Floor<T>): T;

function foo8<U extends number>(x: Floor<U>) {
return foo7(x);
}

Loading
Loading