Skip to content

Commit 03b4df1

Browse files
committed
v3.21.0
1 parent 1413977 commit 03b4df1

File tree

4 files changed

+56
-6
lines changed

4 files changed

+56
-6
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
# 3.21.0
2+
3+
**Features:**
4+
5+
* added `getWellKnownSymbolPropertyOfType` to reliably get symbol named properties due to changes in [email protected]
6+
* `getPropertyNameOfWellKnownSymbol` is now deprecated
7+
18
# 3.20.0
29

310
**Features:**

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "tsutils",
3-
"version": "3.20.0",
3+
"version": "3.21.0",
44
"description": "utilities for working with typescript's AST",
55
"scripts": {
66
"precompile": "rimraf \"{,util,typeguard,test{,/rules}/*.{js,d.ts,js.map}\"",

util/type.ts

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ import {
3030
isShorthandPropertyAssignment,
3131
isEnumMember,
3232
isClassLikeDeclaration,
33+
isInterfaceDeclaration,
34+
isSourceFile,
3335
} from '../typeguard/node';
3436

3537
export function isEmptyObjectType(type: ts.Type): type is ts.ObjectType {
@@ -205,6 +207,29 @@ export function getPropertyOfType(type: ts.Type, name: ts.__String) {
205207
return type.getProperties().find((s) => s.escapedName === name);
206208
}
207209

210+
export function getWellKnownSymbolPropertyOfType(type: ts.Type, wellKnownSymbolName: string, checker: ts.TypeChecker) {
211+
const prefix = '__@' + wellKnownSymbolName;
212+
for (const prop of type.getProperties()) {
213+
if (!prop.name.startsWith(prefix))
214+
continue;
215+
const globalSymbol = checker.getApparentType(
216+
checker.getTypeAtLocation((<ts.ComputedPropertyName>(<ts.NamedDeclaration>prop.valueDeclaration).name).expression),
217+
).symbol;
218+
if (prop.escapedName === getPropertyNameOfWellKnownSymbol(checker, globalSymbol, wellKnownSymbolName))
219+
return prop;
220+
}
221+
return;
222+
}
223+
224+
function getPropertyNameOfWellKnownSymbol(checker: ts.TypeChecker, symbolConstructor: ts.Symbol | undefined, symbolName: string) {
225+
const knownSymbol = symbolConstructor &&
226+
checker.getTypeOfSymbolAtLocation(symbolConstructor, symbolConstructor.valueDeclaration).getProperty(symbolName);
227+
const knownSymbolType = knownSymbol && checker.getTypeOfSymbolAtLocation(knownSymbol, knownSymbol.valueDeclaration);
228+
if (knownSymbolType && isUniqueESSymbolType(knownSymbolType))
229+
return knownSymbolType.escapedName;
230+
return <ts.__String>('__@' + symbolName);
231+
}
232+
208233
/** Determines if writing to a certain property of a given type is allowed. */
209234
export function isPropertyReadonlyInType(type: ts.Type, name: ts.__String, checker: ts.TypeChecker): boolean {
210235
let seenProperty = false;
@@ -285,11 +310,26 @@ export function getPropertyNameFromType(type: ts.Type): PropertyName | undefined
285310
}
286311
if (isUniqueESSymbolType(type))
287312
return {
288-
displayName: `[${type.symbol ? type.symbol.name : (<string>type.escapedName).replace(/^__@|@\d+$/g, '')}]`,
313+
displayName: `[${type.symbol
314+
? `${isKnownSymbol(type.symbol) ? 'Symbol.' : ''}${type.symbol.name}`
315+
: (<string>type.escapedName).replace(/^__@|@\d+$/g, '')
316+
}]`,
289317
symbolName: type.escapedName,
290318
};
291319
}
292320

321+
function isKnownSymbol(symbol: ts.Symbol): boolean {
322+
return isSymbolFlagSet(symbol, ts.SymbolFlags.Property) &&
323+
symbol.valueDeclaration !== undefined &&
324+
isInterfaceDeclaration(symbol.valueDeclaration.parent) &&
325+
symbol.valueDeclaration.parent.name.text === 'SymbolConstructor' &&
326+
isGlobalDeclaration(symbol.valueDeclaration.parent);
327+
}
328+
329+
function isGlobalDeclaration(node: ts.DeclarationStatement): boolean {
330+
return isNodeFlagSet(node.parent!, ts.NodeFlags.GlobalAugmentation) || isSourceFile(node.parent) && !ts.isExternalModule(node.parent);
331+
}
332+
293333
export function getSymbolOfClassLikeDeclaration(node: ts.ClassLikeDeclaration, checker: ts.TypeChecker) {
294334
return checker.getSymbolAtLocation(node.name ?? getChildOfKind(node, ts.SyntaxKind.ClassKeyword)!)!;
295335
}

util/util.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1647,13 +1647,16 @@ export interface PropertyName {
16471647
symbolName: ts.__String;
16481648
}
16491649

1650+
/** @deprecated typescript 4.3 removed the concept of literal well known symbols. Use `getPropertyNameFromType` instead. */
16501651
export function getPropertyNameOfWellKnownSymbol(node: WellKnownSymbolLiteral): PropertyName {
16511652
return {
16521653
displayName: `[Symbol.${node.name.text}]`,
16531654
symbolName: <ts.__String>('__@' + node.name.text),
16541655
};
16551656
}
16561657

1658+
const isTsBefore43 = (([major, minor]) => major < '4' || major === '4' && minor < '3')(ts.versionMajorMinor.split('.'));
1659+
16571660
export interface LateBoundPropertyNames {
16581661
/** Whether all constituents are literal names. */
16591662
known: boolean;
@@ -1667,8 +1670,8 @@ export function getLateBoundPropertyNames(node: ts.Expression, checker: ts.TypeC
16671670
};
16681671

16691672
node = unwrapParentheses(node);
1670-
if (isWellKnownSymbolLiterally(node)) {
1671-
result.names.push(getPropertyNameOfWellKnownSymbol(node));
1673+
if (isTsBefore43 && isWellKnownSymbolLiterally(node)) {
1674+
result.names.push(getPropertyNameOfWellKnownSymbol(node)); // wotan-disable-line no-unstable-api-use
16721675
} else {
16731676
const type = checker.getTypeAtLocation(node);
16741677
for (const key of unionTypeParts(checker.getBaseConstraintOfType(type) || type)) {
@@ -1700,8 +1703,8 @@ export function getSingleLateBoundPropertyNameOfPropertyName(node: ts.PropertyNa
17001703
if (node.kind === ts.SyntaxKind.PrivateIdentifier)
17011704
return {displayName: node.text, symbolName: checker.getSymbolAtLocation(node)!.escapedName};
17021705
const {expression} = <ts.ComputedPropertyName>node;
1703-
return isWellKnownSymbolLiterally(expression)
1704-
? getPropertyNameOfWellKnownSymbol(expression)
1706+
return isTsBefore43 && isWellKnownSymbolLiterally(expression)
1707+
? getPropertyNameOfWellKnownSymbol(expression) // wotan-disable-line no-unstable-api-use
17051708
: getPropertyNameFromType(checker.getTypeAtLocation(expression));
17061709
}
17071710

0 commit comments

Comments
 (0)