Skip to content
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

Support relations and inference between template literal types #43361

Merged
merged 5 commits into from
Mar 28, 2021

Conversation

ahejlsberg
Copy link
Member

With this PR we support relations and inference between template literal types. Specifically, when the target side in a type relation is a template literal type, the source side is now permitted to be a compatible (i.e. more specific) template literal type. Likewise, when inferring to a template literal target type, we now permit the source type to also be a template literal type.

Some examples of improved assignment relations:

declare let s1: `${number}-${number}-${number}`;
declare let s2: `1-2-3`;
declare let s3: `${number}-2-3`;
declare let s4: `1-${number}-3`;
declare let s5: `1-2-${number}`;
declare let s6: `${number}-2-${number}`;
s1 = s2;
s1 = s3;
s1 = s4;
s1 = s5;
s1 = s6;

With this PR all of the above assignments are permitted, where previously only the first assignment was permitted.

Some examples of inference between template literal types:

declare function foo1<V extends string>(arg: `*${V}*`): V;

function test<T extends string>(s: string, n: number, b: boolean, t: T) {
    let x1 = foo('*hello*');  // "hello"
    let x2 = foo('**hello**');  // "*hello*"
    let x3 = foo(`*${s}*` as const);  // string
    let x4 = foo(`*${n}*` as const);  // `${number}`
    let x5 = foo(`*${b}*` as const);  // "true" | "false"
    let x6 = foo(`*${t}*` as const);  // `${T}`
    let x7 = foo(`**${s}**` as const);  // `*${string}*`
}

Fixes #43060.
Fixes #43243.

@typescript-bot typescript-bot added Author: Team For Uncommitted Bug PR for untriaged, rejected, closed or missing bug labels Mar 24, 2021
@ahejlsberg ahejlsberg added this to the TypeScript 4.3.1 milestone Mar 24, 2021
const matches: Type[] = [];
let seg = 0;
let pos = targetStartText.length;
for (let i = 1; i < lastTargetIndex; i++) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some sort of comment (an overview of the algorithm / what it's trying to do, or the name of it if this is a well-known one?) seems justified here

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I think if you could just explain with examples too, that'd be helpful.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I'll add some comments.

}

function getStringLikeTypeForType(type: Type) {
return type.flags & (TypeFlags.Any | TypeFlags.StringLike) ? type : getTemplateLiteralType(["", ""], [type]);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be useful to have a singleton emptyTemplateType?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, the single-element types array varies here, so we can't use a singleton. We could possibly have a singleton for the ["", ""] array, but it's hardly worth the effort as this code doesn't run that often.

return type.flags & (TypeFlags.Any | TypeFlags.StringLike) ? type : getTemplateLiteralType(["", ""], [type]);
}

function inferFromLiteralPartsToTemplateLiteral(sourceTexts: readonly string[], sourceTypes: readonly Type[], target: TemplateLiteralType): Type[] | undefined {
Copy link
Member

@DanielRosenwasser DanielRosenwasser Mar 26, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't like calling these sourceTexts because it immediately signals to me "BUG" even though you're not actually grabbing out the source text. You're grabbing the normalized text content which is the correct thing to do.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I don't think the current names are that bad. After all the properties are called texts and types in template literals and we want the parameters to reflect the relation.

Copy link
Member

@DanielRosenwasser DanielRosenwasser Mar 28, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, I think that even texts would be a better name - I may send a follow-up PR but I'm going to merge this for now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Author: Team For Uncommitted Bug PR for untriaged, rejected, closed or missing bug
Projects
None yet
4 participants