-
Notifications
You must be signed in to change notification settings - Fork 12.8k
Infinite loop -- narrowed down to single file with no imports #26681
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
Comments
Here's a smaller (similar, but different) test case to provide another means of attack. Note the comment that says "Replace this line with the four below and the infinite loop goes away" type PubSubRecordIsStoredInRedisAsA = "redisHash" | "jsonEncodedRedisString";
export interface PubSubRecord<NAME extends string, RECORD, IDENTIFIER extends Partial<RECORD>> {
name: NAME;
record: RECORD;
identifier: IDENTIFIER;
storedAs: PubSubRecordIsStoredInRedisAsA;
maxMsToWaitBeforePublishing: number;
}
type StoredAsConstructor<SO_FAR> =
SO_FAR extends {storedAs: PubSubRecordIsStoredInRedisAsA} ? {} : {
storedAsJsonEncodedRedisString: () => BuildPubSubRecordType<SO_FAR & {storedAs: "jsonEncodedRedisString"}>;
storedRedisHash: () => BuildPubSubRecordType<SO_FAR & {storedAs: "redisHash"}>;
}
const buildStoredAsConstructor = <SO_FAR>(
soFar: SO_FAR
) => (
"storedAs" in soFar ? {} : {
storedAsJsonEncodedRedisString: () =>
buildPubSubRecordType(Object.assign({}, soFar, {storedAs: "jsonEncodedRedisString"})) as
BuildPubSubRecordType<SO_FAR & {storedAs: "jsonEncodedRedisString"}>,
storedAsRedisHash: () =>
buildPubSubRecordType(Object.assign({}, soFar, {storedAs: "redisHash"})) as
BuildPubSubRecordType<SO_FAR & {storedAs: "redisHash"}>,
}
); // Replace this line with the four below and the compiler's infinite loop goes away
// ) as SO_FAR extends {storedAs: PubSubRecordIsStoredInRedisAsA} ? {} : {
// storedAsJsonEncodedRedisString: () => BuildPubSubRecordType<SO_FAR & {storedAs: "jsonEncodedRedisString"}>;
// storedRedisHash: () => BuildPubSubRecordType<SO_FAR & {storedAs: "redisHash"}>;
// };
type IdentifierFieldConstructor<SO_FAR> =
SO_FAR extends {identifier: any} ? {} :
SO_FAR extends {record: any} ? {
identifier: <TYPE extends Partial<SO_FAR["record"]>>(t?: TYPE) => BuildPubSubRecordType<SO_FAR & {identifier: TYPE}>
} : {}
const buildIdentifierFieldConstructor = <SO_FAR>(soFar: SO_FAR) => (
"identifier" in soFar || (!("record" in soFar)) ? {} : {
identifier: <TYPE>(instance: TYPE = undefined) =>
buildPubSubRecordType(Object.assign({}, soFar, {identifier: instance as TYPE}) as SO_FAR & {identifier: TYPE}) as BuildPubSubRecordType<SO_FAR & {identifier: TYPE}>
}
);
type RecordFieldConstructor<SO_FAR> =
SO_FAR extends {record: any} ? {} : {
record: <TYPE>(t?: TYPE) => BuildPubSubRecordType<SO_FAR & {record: TYPE}>
}
const buildRecordFieldConstructor = <SO_FAR>(soFar: SO_FAR) => (
"record" in soFar ? {} : {
record: <TYPE>(instance: TYPE = undefined) =>
buildPubSubRecordType(Object.assign({}, soFar, {record: instance as TYPE}) as SO_FAR & {record: TYPE}) as BuildPubSubRecordType<SO_FAR & {record: TYPE}>
}
);
type MaxMsToWaitBeforePublishingFieldConstructor<SO_FAR> =
SO_FAR extends {maxMsToWaitBeforePublishing: any} ? {} : {
maxMsToWaitBeforePublishing: (t: number) => BuildPubSubRecordType<SO_FAR & {maxMsToWaitBeforePublishing: number}>,
neverDelayPublishing: () => BuildPubSubRecordType<SO_FAR & {maxMsToWaitBeforePublishing: 0}>,
}
const buildMaxMsToWaitBeforePublishingFieldConstructor = <SO_FAR>(soFar: SO_FAR): MaxMsToWaitBeforePublishingFieldConstructor<SO_FAR> => (
"maxMsToWaitBeforePublishing" in soFar ? {} : {
maxMsToWaitBeforePublishing: (instance: number = 0) =>
buildPubSubRecordType(Object.assign({}, soFar, {maxMsToWaitBeforePublishing: instance})) as BuildPubSubRecordType<SO_FAR & {maxMsToWaitBeforePublishing: number}>,
neverDelayPublishing: () =>
buildPubSubRecordType(Object.assign({}, soFar, {maxMsToWaitBeforePublishing: 0})) as BuildPubSubRecordType<SO_FAR & {maxMsToWaitBeforePublishing: 0}>,
}
) as MaxMsToWaitBeforePublishingFieldConstructor<SO_FAR>;
type BuildPubSubRecordType<SO_FAR> =
IdentifierFieldConstructor<SO_FAR> &
RecordFieldConstructor<SO_FAR> &
StoredAsConstructor<SO_FAR> &
MaxMsToWaitBeforePublishingFieldConstructor<SO_FAR>
const buildPubSubRecordType = <SO_FAR>(soFar: SO_FAR) => Object.assign(
{},
buildIdentifierFieldConstructor(soFar),
buildRecordFieldConstructor(soFar),
buildStoredAsConstructor(soFar),
buildMaxMsToWaitBeforePublishingFieldConstructor(soFar),
) as BuildPubSubRecordType<SO_FAR>;
const PubSubRecordType = buildPubSubRecordType({}); |
Preliminary work on a smaller repro: type StoredAsConstructor<SO_FAR> = {
storedAsJsonEncodedRedisString: () => BuildPubSubRecordType<SO_FAR & {storedAs: "jsonEncodedRedisString"}>;
storedRedisHash: () => BuildPubSubRecordType<SO_FAR & {storedAs: "redisHash"}>;
}
const buildStoredAsConstructor = <SO_FAR>(soFar: SO_FAR) => ({
storedAsJsonEncodedRedisString: () => ({}) as BuildPubSubRecordType<SO_FAR & {storedAs: "jsonEncodedRedisString"}>,
});
type IdentifierFieldConstructor<SO_FAR> =
SO_FAR extends {identifier: any} ? {} :
SO_FAR extends {record: any} ? {
identifier: <TYPE extends Partial<SO_FAR["record"]>>(t?: TYPE) => BuildPubSubRecordType<SO_FAR & {identifier: TYPE}>
} : {}
type RecordFieldConstructor<SO_FAR> =
SO_FAR extends {record: any} ? {}
: { record: <TYPE>(t?: TYPE) => BuildPubSubRecordType<SO_FAR & {record: TYPE}> }
type MaxMsToWaitBeforePublishingFieldConstructor<SO_FAR> =
SO_FAR extends {maxMsToWaitBeforePublishing: any} ? {} : {
maxMsToWaitBeforePublishing: (t: number) => BuildPubSubRecordType<SO_FAR & {maxMsToWaitBeforePublishing: number}>,
neverDelayPublishing: () => BuildPubSubRecordType<SO_FAR & {maxMsToWaitBeforePublishing: 0}>,
}
const buildMaxMsToWaitBeforePublishingFieldConstructor = <SO_FAR>(soFar: SO_FAR): MaxMsToWaitBeforePublishingFieldConstructor<SO_FAR> => (
{
neverDelayPublishing: () =>
buildPubSubRecordType(Object.assign({}, soFar, {maxMsToWaitBeforePublishing: 0})) as BuildPubSubRecordType<SO_FAR & {maxMsToWaitBeforePublishing: 0}>,
}
) as MaxMsToWaitBeforePublishingFieldConstructor<SO_FAR>;
type BuildPubSubRecordType<SO_FAR> =
IdentifierFieldConstructor<SO_FAR> &
RecordFieldConstructor<SO_FAR> &
StoredAsConstructor<SO_FAR> &
MaxMsToWaitBeforePublishingFieldConstructor<SO_FAR>
const buildPubSubRecordType = <SO_FAR>(soFar: SO_FAR) => ({}) as BuildPubSubRecordType<SO_FAR>; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
TypeScript Version: [email protected]
I did.
Search Terms: infinite loop
I found one other recent report of an infinite loop, but that reporter wasn't able to isolate the problem. I've got this one nailed to a file with one important and one line that can be commented out to make the infinite loop go away.
Code
Expected behavior:
Compiles.
Actual behavior:
The compiler fails to terminate (at least for as long as I was willing to wait.)
Playground Link:
Related Issues:
Possibly #26612
The text was updated successfully, but these errors were encountered: