Const contexts for template literals (#40707)
* Support const assertions with template literal expressions * Add tests * Accept new baselines
This commit is contained in:
parent
c5a28fcdec
commit
5d6cce5ca7
|
@ -28212,6 +28212,7 @@ namespace ts {
|
|||
case SyntaxKind.FalseKeyword:
|
||||
case SyntaxKind.ArrayLiteralExpression:
|
||||
case SyntaxKind.ObjectLiteralExpression:
|
||||
case SyntaxKind.TemplateExpression:
|
||||
return true;
|
||||
case SyntaxKind.ParenthesizedExpression:
|
||||
return isValidConstAssertionArgument((<ParenthesizedExpression>node).expression);
|
||||
|
@ -30284,18 +30285,17 @@ namespace ts {
|
|||
}
|
||||
|
||||
function checkTemplateExpression(node: TemplateExpression): Type {
|
||||
// We just want to check each expressions, but we are unconcerned with
|
||||
// the type of each expression, as any value may be coerced into a string.
|
||||
// It is worth asking whether this is what we really want though.
|
||||
// A place where we actually *are* concerned with the expressions' types are
|
||||
// in tagged templates.
|
||||
forEach(node.templateSpans, templateSpan => {
|
||||
if (maybeTypeOfKind(checkExpression(templateSpan.expression), TypeFlags.ESSymbolLike)) {
|
||||
error(templateSpan.expression, Diagnostics.Implicit_conversion_of_a_symbol_to_a_string_will_fail_at_runtime_Consider_wrapping_this_expression_in_String);
|
||||
const texts = [node.head.text];
|
||||
const types = [];
|
||||
for (const span of node.templateSpans) {
|
||||
const type = checkExpression(span.expression);
|
||||
if (maybeTypeOfKind(type, TypeFlags.ESSymbolLike)) {
|
||||
error(span.expression, Diagnostics.Implicit_conversion_of_a_symbol_to_a_string_will_fail_at_runtime_Consider_wrapping_this_expression_in_String);
|
||||
}
|
||||
});
|
||||
|
||||
return stringType;
|
||||
texts.push(span.literal.text);
|
||||
types.push(isTypeAssignableTo(type, templateConstraintType) ? type : stringType);
|
||||
}
|
||||
return isConstContext(node) ? getTemplateLiteralType(texts, types) : stringType;
|
||||
}
|
||||
|
||||
function getContextNode(node: Expression): Node {
|
||||
|
@ -30427,7 +30427,7 @@ namespace ts {
|
|||
const parent = node.parent;
|
||||
return isAssertionExpression(parent) && isConstTypeReference(parent.type) ||
|
||||
(isParenthesizedExpression(parent) || isArrayLiteralExpression(parent) || isSpreadElement(parent)) && isConstContext(parent) ||
|
||||
(isPropertyAssignment(parent) || isShorthandPropertyAssignment(parent)) && isConstContext(parent.parent);
|
||||
(isPropertyAssignment(parent) || isShorthandPropertyAssignment(parent) || isTemplateSpan(parent)) && isConstContext(parent.parent);
|
||||
}
|
||||
|
||||
function checkExpressionForMutableLocation(node: Expression, checkMode: CheckMode | undefined, contextualType?: Type, forceTuple?: boolean): Type {
|
||||
|
|
|
@ -77,3 +77,47 @@ tests/cases/conformance/expressions/typeAssertions/constAssertions.ts(63,10): er
|
|||
~~~~~
|
||||
!!! error TS1355: A 'const' assertions can only be applied to references to enum members, or string, number, boolean, array, or object literals.
|
||||
|
||||
let t1 = 'foo' as const;
|
||||
let t2 = 'bar' as const;
|
||||
let t3 = `${t1}-${t2}` as const;
|
||||
let t4 = `${`(${t1})`}-${`(${t2})`}` as const;
|
||||
|
||||
function ff1(x: 'foo' | 'bar', y: 1 | 2) {
|
||||
return `${x}-${y}` as const;
|
||||
}
|
||||
|
||||
function ff2<T extends string, U extends string>(x: T, y: U) {
|
||||
return `${x}-${y}` as const;
|
||||
}
|
||||
|
||||
const ts1 = ff2('foo', 'bar');
|
||||
const ts2 = ff2('foo', !!true ? '0' : '1');
|
||||
const ts3 = ff2(!!true ? 'top' : 'bottom', !!true ? 'left' : 'right');
|
||||
|
||||
function ff3(x: 'foo' | 'bar', y: object) {
|
||||
return `${x}${y}` as const;
|
||||
}
|
||||
|
||||
type Action = "verify" | "write";
|
||||
type ContentMatch = "match" | "nonMatch";
|
||||
type Outcome = `${Action}_${ContentMatch}`;
|
||||
|
||||
function ff4(verify: boolean, contentMatches: boolean) {
|
||||
const action : Action = verify ? `verify` : `write`;
|
||||
const contentMatch: ContentMatch = contentMatches ? `match` : `nonMatch`;
|
||||
const outcome: Outcome = `${action}_${contentMatch}` as const;
|
||||
return outcome;
|
||||
}
|
||||
|
||||
function ff5(verify: boolean, contentMatches: boolean) {
|
||||
const action = verify ? `verify` : `write`;
|
||||
const contentMatch = contentMatches ? `match` : `nonMatch`;
|
||||
const outcome = `${action}_${contentMatch}` as const;
|
||||
return outcome;
|
||||
}
|
||||
|
||||
function accessorNames<S extends string>(propName: S) {
|
||||
return [`get-${propName}`, `set-${propName}`] as const;
|
||||
}
|
||||
|
||||
const ns1 = accessorNames('foo');
|
|
@ -63,6 +63,50 @@ let e1 = v1 as const; // Error
|
|||
let e2 = (true ? 1 : 0) as const; // Error
|
||||
let e3 = id(1) as const; // Error
|
||||
|
||||
let t1 = 'foo' as const;
|
||||
let t2 = 'bar' as const;
|
||||
let t3 = `${t1}-${t2}` as const;
|
||||
let t4 = `${`(${t1})`}-${`(${t2})`}` as const;
|
||||
|
||||
function ff1(x: 'foo' | 'bar', y: 1 | 2) {
|
||||
return `${x}-${y}` as const;
|
||||
}
|
||||
|
||||
function ff2<T extends string, U extends string>(x: T, y: U) {
|
||||
return `${x}-${y}` as const;
|
||||
}
|
||||
|
||||
const ts1 = ff2('foo', 'bar');
|
||||
const ts2 = ff2('foo', !!true ? '0' : '1');
|
||||
const ts3 = ff2(!!true ? 'top' : 'bottom', !!true ? 'left' : 'right');
|
||||
|
||||
function ff3(x: 'foo' | 'bar', y: object) {
|
||||
return `${x}${y}` as const;
|
||||
}
|
||||
|
||||
type Action = "verify" | "write";
|
||||
type ContentMatch = "match" | "nonMatch";
|
||||
type Outcome = `${Action}_${ContentMatch}`;
|
||||
|
||||
function ff4(verify: boolean, contentMatches: boolean) {
|
||||
const action : Action = verify ? `verify` : `write`;
|
||||
const contentMatch: ContentMatch = contentMatches ? `match` : `nonMatch`;
|
||||
const outcome: Outcome = `${action}_${contentMatch}` as const;
|
||||
return outcome;
|
||||
}
|
||||
|
||||
function ff5(verify: boolean, contentMatches: boolean) {
|
||||
const action = verify ? `verify` : `write`;
|
||||
const contentMatch = contentMatches ? `match` : `nonMatch`;
|
||||
const outcome = `${action}_${contentMatch}` as const;
|
||||
return outcome;
|
||||
}
|
||||
|
||||
function accessorNames<S extends string>(propName: S) {
|
||||
return [`get-${propName}`, `set-${propName}`] as const;
|
||||
}
|
||||
|
||||
const ns1 = accessorNames('foo');
|
||||
|
||||
//// [constAssertions.js]
|
||||
"use strict";
|
||||
|
@ -117,6 +161,38 @@ let q5 = { x: 10, y: 20 };
|
|||
let e1 = v1; // Error
|
||||
let e2 = (true ? 1 : 0); // Error
|
||||
let e3 = id(1); // Error
|
||||
let t1 = 'foo';
|
||||
let t2 = 'bar';
|
||||
let t3 = `${t1}-${t2}`;
|
||||
let t4 = `${`(${t1})`}-${`(${t2})`}`;
|
||||
function ff1(x, y) {
|
||||
return `${x}-${y}`;
|
||||
}
|
||||
function ff2(x, y) {
|
||||
return `${x}-${y}`;
|
||||
}
|
||||
const ts1 = ff2('foo', 'bar');
|
||||
const ts2 = ff2('foo', !!true ? '0' : '1');
|
||||
const ts3 = ff2(!!true ? 'top' : 'bottom', !!true ? 'left' : 'right');
|
||||
function ff3(x, y) {
|
||||
return `${x}${y}`;
|
||||
}
|
||||
function ff4(verify, contentMatches) {
|
||||
const action = verify ? `verify` : `write`;
|
||||
const contentMatch = contentMatches ? `match` : `nonMatch`;
|
||||
const outcome = `${action}_${contentMatch}`;
|
||||
return outcome;
|
||||
}
|
||||
function ff5(verify, contentMatches) {
|
||||
const action = verify ? `verify` : `write`;
|
||||
const contentMatch = contentMatches ? `match` : `nonMatch`;
|
||||
const outcome = `${action}_${contentMatch}`;
|
||||
return outcome;
|
||||
}
|
||||
function accessorNames(propName) {
|
||||
return [`get-${propName}`, `set-${propName}`];
|
||||
}
|
||||
const ns1 = accessorNames('foo');
|
||||
|
||||
|
||||
//// [constAssertions.d.ts]
|
||||
|
@ -218,3 +294,20 @@ declare function id<T>(x: T): T;
|
|||
declare let e1: "abc";
|
||||
declare let e2: 0 | 1;
|
||||
declare let e3: 1;
|
||||
declare let t1: "foo";
|
||||
declare let t2: "bar";
|
||||
declare let t3: "foo-bar";
|
||||
declare let t4: "(foo)-(bar)";
|
||||
declare function ff1(x: 'foo' | 'bar', y: 1 | 2): "foo-1" | "foo-2" | "bar-1" | "bar-2";
|
||||
declare function ff2<T extends string, U extends string>(x: T, y: U): `${T}-${U}`;
|
||||
declare const ts1: "foo-bar";
|
||||
declare const ts2: "foo-1" | "foo-0";
|
||||
declare const ts3: "top-left" | "top-right" | "bottom-left" | "bottom-right";
|
||||
declare function ff3(x: 'foo' | 'bar', y: object): string;
|
||||
declare type Action = "verify" | "write";
|
||||
declare type ContentMatch = "match" | "nonMatch";
|
||||
declare type Outcome = `${Action}_${ContentMatch}`;
|
||||
declare function ff4(verify: boolean, contentMatches: boolean): "verify_match" | "verify_nonMatch" | "write_match" | "write_nonMatch";
|
||||
declare function ff5(verify: boolean, contentMatches: boolean): "verify_match" | "verify_nonMatch" | "write_match" | "write_nonMatch";
|
||||
declare function accessorNames<S extends string>(propName: S): readonly [`get-${S}`, `set-${S}`];
|
||||
declare const ns1: readonly ["get-foo", "set-foo"];
|
||||
|
|
|
@ -199,3 +199,138 @@ let e3 = id(1) as const; // Error
|
|||
>e3 : Symbol(e3, Decl(constAssertions.ts, 62, 3))
|
||||
>id : Symbol(id, Decl(constAssertions.ts, 56, 34))
|
||||
|
||||
let t1 = 'foo' as const;
|
||||
>t1 : Symbol(t1, Decl(constAssertions.ts, 64, 3))
|
||||
|
||||
let t2 = 'bar' as const;
|
||||
>t2 : Symbol(t2, Decl(constAssertions.ts, 65, 3))
|
||||
|
||||
let t3 = `${t1}-${t2}` as const;
|
||||
>t3 : Symbol(t3, Decl(constAssertions.ts, 66, 3))
|
||||
>t1 : Symbol(t1, Decl(constAssertions.ts, 64, 3))
|
||||
>t2 : Symbol(t2, Decl(constAssertions.ts, 65, 3))
|
||||
|
||||
let t4 = `${`(${t1})`}-${`(${t2})`}` as const;
|
||||
>t4 : Symbol(t4, Decl(constAssertions.ts, 67, 3))
|
||||
>t1 : Symbol(t1, Decl(constAssertions.ts, 64, 3))
|
||||
>t2 : Symbol(t2, Decl(constAssertions.ts, 65, 3))
|
||||
|
||||
function ff1(x: 'foo' | 'bar', y: 1 | 2) {
|
||||
>ff1 : Symbol(ff1, Decl(constAssertions.ts, 67, 46))
|
||||
>x : Symbol(x, Decl(constAssertions.ts, 69, 13))
|
||||
>y : Symbol(y, Decl(constAssertions.ts, 69, 30))
|
||||
|
||||
return `${x}-${y}` as const;
|
||||
>x : Symbol(x, Decl(constAssertions.ts, 69, 13))
|
||||
>y : Symbol(y, Decl(constAssertions.ts, 69, 30))
|
||||
}
|
||||
|
||||
function ff2<T extends string, U extends string>(x: T, y: U) {
|
||||
>ff2 : Symbol(ff2, Decl(constAssertions.ts, 71, 1))
|
||||
>T : Symbol(T, Decl(constAssertions.ts, 73, 13))
|
||||
>U : Symbol(U, Decl(constAssertions.ts, 73, 30))
|
||||
>x : Symbol(x, Decl(constAssertions.ts, 73, 49))
|
||||
>T : Symbol(T, Decl(constAssertions.ts, 73, 13))
|
||||
>y : Symbol(y, Decl(constAssertions.ts, 73, 54))
|
||||
>U : Symbol(U, Decl(constAssertions.ts, 73, 30))
|
||||
|
||||
return `${x}-${y}` as const;
|
||||
>x : Symbol(x, Decl(constAssertions.ts, 73, 49))
|
||||
>y : Symbol(y, Decl(constAssertions.ts, 73, 54))
|
||||
}
|
||||
|
||||
const ts1 = ff2('foo', 'bar');
|
||||
>ts1 : Symbol(ts1, Decl(constAssertions.ts, 77, 5))
|
||||
>ff2 : Symbol(ff2, Decl(constAssertions.ts, 71, 1))
|
||||
|
||||
const ts2 = ff2('foo', !!true ? '0' : '1');
|
||||
>ts2 : Symbol(ts2, Decl(constAssertions.ts, 78, 5))
|
||||
>ff2 : Symbol(ff2, Decl(constAssertions.ts, 71, 1))
|
||||
|
||||
const ts3 = ff2(!!true ? 'top' : 'bottom', !!true ? 'left' : 'right');
|
||||
>ts3 : Symbol(ts3, Decl(constAssertions.ts, 79, 5))
|
||||
>ff2 : Symbol(ff2, Decl(constAssertions.ts, 71, 1))
|
||||
|
||||
function ff3(x: 'foo' | 'bar', y: object) {
|
||||
>ff3 : Symbol(ff3, Decl(constAssertions.ts, 79, 70))
|
||||
>x : Symbol(x, Decl(constAssertions.ts, 81, 13))
|
||||
>y : Symbol(y, Decl(constAssertions.ts, 81, 30))
|
||||
|
||||
return `${x}${y}` as const;
|
||||
>x : Symbol(x, Decl(constAssertions.ts, 81, 13))
|
||||
>y : Symbol(y, Decl(constAssertions.ts, 81, 30))
|
||||
}
|
||||
|
||||
type Action = "verify" | "write";
|
||||
>Action : Symbol(Action, Decl(constAssertions.ts, 83, 1))
|
||||
|
||||
type ContentMatch = "match" | "nonMatch";
|
||||
>ContentMatch : Symbol(ContentMatch, Decl(constAssertions.ts, 85, 33))
|
||||
|
||||
type Outcome = `${Action}_${ContentMatch}`;
|
||||
>Outcome : Symbol(Outcome, Decl(constAssertions.ts, 86, 41))
|
||||
>Action : Symbol(Action, Decl(constAssertions.ts, 83, 1))
|
||||
>ContentMatch : Symbol(ContentMatch, Decl(constAssertions.ts, 85, 33))
|
||||
|
||||
function ff4(verify: boolean, contentMatches: boolean) {
|
||||
>ff4 : Symbol(ff4, Decl(constAssertions.ts, 87, 43))
|
||||
>verify : Symbol(verify, Decl(constAssertions.ts, 89, 13))
|
||||
>contentMatches : Symbol(contentMatches, Decl(constAssertions.ts, 89, 29))
|
||||
|
||||
const action : Action = verify ? `verify` : `write`;
|
||||
>action : Symbol(action, Decl(constAssertions.ts, 90, 9))
|
||||
>Action : Symbol(Action, Decl(constAssertions.ts, 83, 1))
|
||||
>verify : Symbol(verify, Decl(constAssertions.ts, 89, 13))
|
||||
|
||||
const contentMatch: ContentMatch = contentMatches ? `match` : `nonMatch`;
|
||||
>contentMatch : Symbol(contentMatch, Decl(constAssertions.ts, 91, 9))
|
||||
>ContentMatch : Symbol(ContentMatch, Decl(constAssertions.ts, 85, 33))
|
||||
>contentMatches : Symbol(contentMatches, Decl(constAssertions.ts, 89, 29))
|
||||
|
||||
const outcome: Outcome = `${action}_${contentMatch}` as const;
|
||||
>outcome : Symbol(outcome, Decl(constAssertions.ts, 92, 9))
|
||||
>Outcome : Symbol(Outcome, Decl(constAssertions.ts, 86, 41))
|
||||
>action : Symbol(action, Decl(constAssertions.ts, 90, 9))
|
||||
>contentMatch : Symbol(contentMatch, Decl(constAssertions.ts, 91, 9))
|
||||
|
||||
return outcome;
|
||||
>outcome : Symbol(outcome, Decl(constAssertions.ts, 92, 9))
|
||||
}
|
||||
|
||||
function ff5(verify: boolean, contentMatches: boolean) {
|
||||
>ff5 : Symbol(ff5, Decl(constAssertions.ts, 94, 1))
|
||||
>verify : Symbol(verify, Decl(constAssertions.ts, 96, 13))
|
||||
>contentMatches : Symbol(contentMatches, Decl(constAssertions.ts, 96, 29))
|
||||
|
||||
const action = verify ? `verify` : `write`;
|
||||
>action : Symbol(action, Decl(constAssertions.ts, 97, 9))
|
||||
>verify : Symbol(verify, Decl(constAssertions.ts, 96, 13))
|
||||
|
||||
const contentMatch = contentMatches ? `match` : `nonMatch`;
|
||||
>contentMatch : Symbol(contentMatch, Decl(constAssertions.ts, 98, 9))
|
||||
>contentMatches : Symbol(contentMatches, Decl(constAssertions.ts, 96, 29))
|
||||
|
||||
const outcome = `${action}_${contentMatch}` as const;
|
||||
>outcome : Symbol(outcome, Decl(constAssertions.ts, 99, 9))
|
||||
>action : Symbol(action, Decl(constAssertions.ts, 97, 9))
|
||||
>contentMatch : Symbol(contentMatch, Decl(constAssertions.ts, 98, 9))
|
||||
|
||||
return outcome;
|
||||
>outcome : Symbol(outcome, Decl(constAssertions.ts, 99, 9))
|
||||
}
|
||||
|
||||
function accessorNames<S extends string>(propName: S) {
|
||||
>accessorNames : Symbol(accessorNames, Decl(constAssertions.ts, 101, 1))
|
||||
>S : Symbol(S, Decl(constAssertions.ts, 103, 23))
|
||||
>propName : Symbol(propName, Decl(constAssertions.ts, 103, 41))
|
||||
>S : Symbol(S, Decl(constAssertions.ts, 103, 23))
|
||||
|
||||
return [`get-${propName}`, `set-${propName}`] as const;
|
||||
>propName : Symbol(propName, Decl(constAssertions.ts, 103, 41))
|
||||
>propName : Symbol(propName, Decl(constAssertions.ts, 103, 41))
|
||||
}
|
||||
|
||||
const ns1 = accessorNames('foo');
|
||||
>ns1 : Symbol(ns1, Decl(constAssertions.ts, 107, 5))
|
||||
>accessorNames : Symbol(accessorNames, Decl(constAssertions.ts, 101, 1))
|
||||
|
||||
|
|
|
@ -354,3 +354,189 @@ let e3 = id(1) as const; // Error
|
|||
>id : <T>(x: T) => T
|
||||
>1 : 1
|
||||
|
||||
let t1 = 'foo' as const;
|
||||
>t1 : "foo"
|
||||
>'foo' as const : "foo"
|
||||
>'foo' : "foo"
|
||||
|
||||
let t2 = 'bar' as const;
|
||||
>t2 : "bar"
|
||||
>'bar' as const : "bar"
|
||||
>'bar' : "bar"
|
||||
|
||||
let t3 = `${t1}-${t2}` as const;
|
||||
>t3 : "foo-bar"
|
||||
>`${t1}-${t2}` as const : "foo-bar"
|
||||
>`${t1}-${t2}` : "foo-bar"
|
||||
>t1 : "foo"
|
||||
>t2 : "bar"
|
||||
|
||||
let t4 = `${`(${t1})`}-${`(${t2})`}` as const;
|
||||
>t4 : "(foo)-(bar)"
|
||||
>`${`(${t1})`}-${`(${t2})`}` as const : "(foo)-(bar)"
|
||||
>`${`(${t1})`}-${`(${t2})`}` : "(foo)-(bar)"
|
||||
>`(${t1})` : "(foo)"
|
||||
>t1 : "foo"
|
||||
>`(${t2})` : "(bar)"
|
||||
>t2 : "bar"
|
||||
|
||||
function ff1(x: 'foo' | 'bar', y: 1 | 2) {
|
||||
>ff1 : (x: 'foo' | 'bar', y: 1 | 2) => "foo-1" | "foo-2" | "bar-1" | "bar-2"
|
||||
>x : "foo" | "bar"
|
||||
>y : 1 | 2
|
||||
|
||||
return `${x}-${y}` as const;
|
||||
>`${x}-${y}` as const : "foo-1" | "foo-2" | "bar-1" | "bar-2"
|
||||
>`${x}-${y}` : "foo-1" | "foo-2" | "bar-1" | "bar-2"
|
||||
>x : "foo" | "bar"
|
||||
>y : 1 | 2
|
||||
}
|
||||
|
||||
function ff2<T extends string, U extends string>(x: T, y: U) {
|
||||
>ff2 : <T extends string, U extends string>(x: T, y: U) => `${T}-${U}`
|
||||
>x : T
|
||||
>y : U
|
||||
|
||||
return `${x}-${y}` as const;
|
||||
>`${x}-${y}` as const : `${T}-${U}`
|
||||
>`${x}-${y}` : `${T}-${U}`
|
||||
>x : T
|
||||
>y : U
|
||||
}
|
||||
|
||||
const ts1 = ff2('foo', 'bar');
|
||||
>ts1 : "foo-bar"
|
||||
>ff2('foo', 'bar') : "foo-bar"
|
||||
>ff2 : <T extends string, U extends string>(x: T, y: U) => `${T}-${U}`
|
||||
>'foo' : "foo"
|
||||
>'bar' : "bar"
|
||||
|
||||
const ts2 = ff2('foo', !!true ? '0' : '1');
|
||||
>ts2 : "foo-1" | "foo-0"
|
||||
>ff2('foo', !!true ? '0' : '1') : "foo-1" | "foo-0"
|
||||
>ff2 : <T extends string, U extends string>(x: T, y: U) => `${T}-${U}`
|
||||
>'foo' : "foo"
|
||||
>!!true ? '0' : '1' : "0" | "1"
|
||||
>!!true : true
|
||||
>!true : false
|
||||
>true : true
|
||||
>'0' : "0"
|
||||
>'1' : "1"
|
||||
|
||||
const ts3 = ff2(!!true ? 'top' : 'bottom', !!true ? 'left' : 'right');
|
||||
>ts3 : "top-left" | "top-right" | "bottom-left" | "bottom-right"
|
||||
>ff2(!!true ? 'top' : 'bottom', !!true ? 'left' : 'right') : "top-left" | "top-right" | "bottom-left" | "bottom-right"
|
||||
>ff2 : <T extends string, U extends string>(x: T, y: U) => `${T}-${U}`
|
||||
>!!true ? 'top' : 'bottom' : "top" | "bottom"
|
||||
>!!true : true
|
||||
>!true : false
|
||||
>true : true
|
||||
>'top' : "top"
|
||||
>'bottom' : "bottom"
|
||||
>!!true ? 'left' : 'right' : "left" | "right"
|
||||
>!!true : true
|
||||
>!true : false
|
||||
>true : true
|
||||
>'left' : "left"
|
||||
>'right' : "right"
|
||||
|
||||
function ff3(x: 'foo' | 'bar', y: object) {
|
||||
>ff3 : (x: 'foo' | 'bar', y: object) => string
|
||||
>x : "foo" | "bar"
|
||||
>y : object
|
||||
|
||||
return `${x}${y}` as const;
|
||||
>`${x}${y}` as const : string
|
||||
>`${x}${y}` : string
|
||||
>x : "foo" | "bar"
|
||||
>y : object
|
||||
}
|
||||
|
||||
type Action = "verify" | "write";
|
||||
>Action : Action
|
||||
|
||||
type ContentMatch = "match" | "nonMatch";
|
||||
>ContentMatch : ContentMatch
|
||||
|
||||
type Outcome = `${Action}_${ContentMatch}`;
|
||||
>Outcome : "verify_match" | "verify_nonMatch" | "write_match" | "write_nonMatch"
|
||||
|
||||
function ff4(verify: boolean, contentMatches: boolean) {
|
||||
>ff4 : (verify: boolean, contentMatches: boolean) => "verify_match" | "verify_nonMatch" | "write_match" | "write_nonMatch"
|
||||
>verify : boolean
|
||||
>contentMatches : boolean
|
||||
|
||||
const action : Action = verify ? `verify` : `write`;
|
||||
>action : Action
|
||||
>verify ? `verify` : `write` : Action
|
||||
>verify : boolean
|
||||
>`verify` : "verify"
|
||||
>`write` : "write"
|
||||
|
||||
const contentMatch: ContentMatch = contentMatches ? `match` : `nonMatch`;
|
||||
>contentMatch : ContentMatch
|
||||
>contentMatches ? `match` : `nonMatch` : ContentMatch
|
||||
>contentMatches : boolean
|
||||
>`match` : "match"
|
||||
>`nonMatch` : "nonMatch"
|
||||
|
||||
const outcome: Outcome = `${action}_${contentMatch}` as const;
|
||||
>outcome : "verify_match" | "verify_nonMatch" | "write_match" | "write_nonMatch"
|
||||
>`${action}_${contentMatch}` as const : "verify_match" | "verify_nonMatch" | "write_match" | "write_nonMatch"
|
||||
>`${action}_${contentMatch}` : "verify_match" | "verify_nonMatch" | "write_match" | "write_nonMatch"
|
||||
>action : Action
|
||||
>contentMatch : ContentMatch
|
||||
|
||||
return outcome;
|
||||
>outcome : "verify_match" | "verify_nonMatch" | "write_match" | "write_nonMatch"
|
||||
}
|
||||
|
||||
function ff5(verify: boolean, contentMatches: boolean) {
|
||||
>ff5 : (verify: boolean, contentMatches: boolean) => "verify_match" | "verify_nonMatch" | "write_match" | "write_nonMatch"
|
||||
>verify : boolean
|
||||
>contentMatches : boolean
|
||||
|
||||
const action = verify ? `verify` : `write`;
|
||||
>action : "verify" | "write"
|
||||
>verify ? `verify` : `write` : Action
|
||||
>verify : boolean
|
||||
>`verify` : "verify"
|
||||
>`write` : "write"
|
||||
|
||||
const contentMatch = contentMatches ? `match` : `nonMatch`;
|
||||
>contentMatch : "match" | "nonMatch"
|
||||
>contentMatches ? `match` : `nonMatch` : ContentMatch
|
||||
>contentMatches : boolean
|
||||
>`match` : "match"
|
||||
>`nonMatch` : "nonMatch"
|
||||
|
||||
const outcome = `${action}_${contentMatch}` as const;
|
||||
>outcome : "verify_match" | "verify_nonMatch" | "write_match" | "write_nonMatch"
|
||||
>`${action}_${contentMatch}` as const : "verify_match" | "verify_nonMatch" | "write_match" | "write_nonMatch"
|
||||
>`${action}_${contentMatch}` : "verify_match" | "verify_nonMatch" | "write_match" | "write_nonMatch"
|
||||
>action : Action
|
||||
>contentMatch : ContentMatch
|
||||
|
||||
return outcome;
|
||||
>outcome : "verify_match" | "verify_nonMatch" | "write_match" | "write_nonMatch"
|
||||
}
|
||||
|
||||
function accessorNames<S extends string>(propName: S) {
|
||||
>accessorNames : <S extends string>(propName: S) => readonly [`get-${S}`, `set-${S}`]
|
||||
>propName : S
|
||||
|
||||
return [`get-${propName}`, `set-${propName}`] as const;
|
||||
>[`get-${propName}`, `set-${propName}`] as const : readonly [`get-${S}`, `set-${S}`]
|
||||
>[`get-${propName}`, `set-${propName}`] : readonly [`get-${S}`, `set-${S}`]
|
||||
>`get-${propName}` : `get-${S}`
|
||||
>propName : S
|
||||
>`set-${propName}` : `set-${S}`
|
||||
>propName : S
|
||||
}
|
||||
|
||||
const ns1 = accessorNames('foo');
|
||||
>ns1 : readonly ["get-foo", "set-foo"]
|
||||
>accessorNames('foo') : readonly ["get-foo", "set-foo"]
|
||||
>accessorNames : <S extends string>(propName: S) => readonly [`get-${S}`, `set-${S}`]
|
||||
>'foo' : "foo"
|
||||
|
||||
|
|
|
@ -65,3 +65,48 @@ declare function id<T>(x: T): T;
|
|||
let e1 = v1 as const; // Error
|
||||
let e2 = (true ? 1 : 0) as const; // Error
|
||||
let e3 = id(1) as const; // Error
|
||||
|
||||
let t1 = 'foo' as const;
|
||||
let t2 = 'bar' as const;
|
||||
let t3 = `${t1}-${t2}` as const;
|
||||
let t4 = `${`(${t1})`}-${`(${t2})`}` as const;
|
||||
|
||||
function ff1(x: 'foo' | 'bar', y: 1 | 2) {
|
||||
return `${x}-${y}` as const;
|
||||
}
|
||||
|
||||
function ff2<T extends string, U extends string>(x: T, y: U) {
|
||||
return `${x}-${y}` as const;
|
||||
}
|
||||
|
||||
const ts1 = ff2('foo', 'bar');
|
||||
const ts2 = ff2('foo', !!true ? '0' : '1');
|
||||
const ts3 = ff2(!!true ? 'top' : 'bottom', !!true ? 'left' : 'right');
|
||||
|
||||
function ff3(x: 'foo' | 'bar', y: object) {
|
||||
return `${x}${y}` as const;
|
||||
}
|
||||
|
||||
type Action = "verify" | "write";
|
||||
type ContentMatch = "match" | "nonMatch";
|
||||
type Outcome = `${Action}_${ContentMatch}`;
|
||||
|
||||
function ff4(verify: boolean, contentMatches: boolean) {
|
||||
const action : Action = verify ? `verify` : `write`;
|
||||
const contentMatch: ContentMatch = contentMatches ? `match` : `nonMatch`;
|
||||
const outcome: Outcome = `${action}_${contentMatch}` as const;
|
||||
return outcome;
|
||||
}
|
||||
|
||||
function ff5(verify: boolean, contentMatches: boolean) {
|
||||
const action = verify ? `verify` : `write`;
|
||||
const contentMatch = contentMatches ? `match` : `nonMatch`;
|
||||
const outcome = `${action}_${contentMatch}` as const;
|
||||
return outcome;
|
||||
}
|
||||
|
||||
function accessorNames<S extends string>(propName: S) {
|
||||
return [`get-${propName}`, `set-${propName}`] as const;
|
||||
}
|
||||
|
||||
const ns1 = accessorNames('foo');
|
Loading…
Reference in a new issue