Skip to content

Commit

Permalink
feat(vscode): support mapped types
Browse files Browse the repository at this point in the history
  • Loading branch information
mxsdev committed Oct 10, 2022
1 parent a02fb73 commit 56942d9
Show file tree
Hide file tree
Showing 12 changed files with 73 additions and 17 deletions.
9 changes: 7 additions & 2 deletions packages/api/src/tree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -206,12 +206,17 @@ function _generateTypeTree({ symbol, type }: SymbolOrType, ctx: TypeTreeContext,

function getIndexInfo(indexInfo: TSIndexInfoMerged): IndexInfo {
const { typeChecker } = ctx

const parameterSymbol =
// @ts-expect-error
indexInfo?.declaration?.parameters?.[0]?.symbol
?? indexInfo?.parameterType?.getSymbol()

return {
...indexInfo.keyType && { keyType: parseType(indexInfo.keyType) },
...indexInfo.type && { type: parseType(indexInfo.type) },
// @ts-expect-error
parameterSymbol: wrapSafe(getSymbolInfo)(wrapSafe(typeChecker.getSymbolAtLocation)(indexInfo?.declaration?.parameters?.[0]))
// parameterSymbol: wrapSafe(getSymbolInfo)(wrapSafe(typeChecker.getSymbolAtLocation)(indexInfo?.declaration?.parameters?.[0]))
parameterSymbol: wrapSafe(getSymbolInfo)(parameterSymbol),
}
}

Expand Down
1 change: 1 addition & 0 deletions packages/api/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export type IndexInfo = {
keyType?: TypeInfo,
type?: TypeInfo,
parameterSymbol?: SymbolInfo
// parameterName?: string
}

// export type FunctionParameterInfo = {
Expand Down
2 changes: 1 addition & 1 deletion packages/api/src/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ interface MappedType extends ts.Type {
export function getIndexInfos(typeChecker: ts.TypeChecker, type: ts.Type) {
const indexInfos: TSIndexInfoMerged[] = [ ...typeChecker.getIndexInfosOfType(type) ]

if((type.flags & ts.TypeFlags.Object) && ((type as ObjectType).objectFlags & ts.ObjectFlags.Mapped) && !((type as ObjectType).objectFlags & ts.ObjectFlags.Instantiated)) {
if(indexInfos.length === 0 && (type.flags & ts.TypeFlags.Object) && ((type as ObjectType).objectFlags & ts.ObjectFlags.Mapped) && !((type as ObjectType).objectFlags & ts.ObjectFlags.Instantiated)) {
const mappedType = type as MappedType

if(mappedType.typeParameter) {
Expand Down
2 changes: 1 addition & 1 deletion packages/typescript-explorer/src/localization.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export const KindText: Record<Kind, string> = {
"enum_literal": "$1",
"number_literal": "$1",
"conditional": "conditional",
"index": "Index",
"index": "keyof",
"indexed_access": "access",
"intersection": "intersection",
"union": "union",
Expand Down
49 changes: 40 additions & 9 deletions packages/typescript-explorer/src/view/typeTreeView.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { TypeInfo, TypeId, getTypeInfoChildren, SymbolInfo, SignatureInfo } from '@ts-expand-type/api'
import { TypeInfo, TypeId, getTypeInfoChildren, SymbolInfo, SignatureInfo, IndexInfo } from '@ts-expand-type/api'
import assert = require('assert');
import * as vscode from 'vscode'
import * as ts from 'typescript'
Expand Down Expand Up @@ -87,6 +87,10 @@ abstract class TypeTreeItem extends vscode.TreeItem {
return new SignatureNode(signature, this.provider, this)
}

createIndexNode(indexInfo: IndexInfo) {
return new IndexNode(indexInfo, this.provider, this)
}

getSignatureChildren(signature: SignatureInfo): TypeNode[] {
return [
...signature.parameters.map(param => this.createChildTypeNode(param)),
Expand Down Expand Up @@ -136,8 +140,11 @@ class TypeNode extends TypeTreeItem {

switch(kind) {
case "object": {
const { properties, indexInfos } = this.typeTree
return properties.map(toTreeNode)
const { properties, indexInfos = [] } = this.typeTree
return [
...indexInfos.map(info => this.createIndexNode(info)),
...properties.map(toTreeNode),
]
}

case "function": {
Expand Down Expand Up @@ -194,9 +201,27 @@ class SignatureNode extends TypeTreeItem {
}
}

class IndexNode extends TypeTreeItem {
constructor(
private indexInfo: IndexInfo,
provider: TypeTreeProvider,
parent: TypeTreeItem|undefined
) {
super(indexInfo.parameterSymbol?.name ?? "", vscode.TreeItemCollapsibleState.Collapsed, provider, parent)
this.description = "index"
}

getChildren() {
return [
...this.indexInfo.keyType ? [this.createChildTypeNode(this.indexInfo.keyType, { purpose: 'index_type'})] : [],
...this.indexInfo.type ? [this.createChildTypeNode(this.indexInfo.type, { purpose: 'index_value_type'})] : [],
]
}
}

type TypeNodeArgs = {
purpose?: 'return',
optional?: boolean
purpose?: 'return'|'index_type'|'index_value_type',
optional?: boolean,
}

class TypeNodeGroup extends TypeTreeItem {
Expand All @@ -214,8 +239,8 @@ class TypeNodeGroup extends TypeTreeItem {
}
}

function generateTypeNodeMeta(info: ResolvedTypeInfo, dimension: number, args?: TypeNodeArgs) {
const isOptional = info.symbolMeta?.optional || args?.optional || ((info.symbolMeta?.flags ?? 0) & ts.SymbolFlags.Optional)
function generateTypeNodeMeta(info: ResolvedTypeInfo, dimension: number, {purpose, optional}: TypeNodeArgs = {}) {
const isOptional = info.symbolMeta?.optional || optional || ((info.symbolMeta?.flags ?? 0) & ts.SymbolFlags.Optional)

let description = getBaseDescription()
description += "[]".repeat(dimension)
Expand All @@ -232,8 +257,14 @@ function generateTypeNodeMeta(info: ResolvedTypeInfo, dimension: number, args?:
}

function getLabel() {
if(args?.purpose === 'return') {
return "<return>"
const nameByPurpose: Partial<Record<NonNullable<TypeNodeArgs['purpose']>, string>> = {
return: "<return>",
index_type: "<index type>",
index_value_type: "<type>",
}

if(purpose && purpose in nameByPurpose) {
return nameByPurpose[purpose]!
}

return !info.symbolMeta?.anonymous ? (info.symbolMeta?.name ?? "") : ""
Expand Down
2 changes: 1 addition & 1 deletion tests/baselines/reference/consoleLog.tree

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion tests/baselines/reference/mapped.tree
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
=== mapped.ts ===

type mapped = { [index: string]: number }
> mapped --- {"kind":"object","properties":[],"indexInfos":[{"keyType":{"kind":"primitive","primitive":"string","id":15},"type":{"kind":"primitive","primitive":"number","id":16}}],"symbolMeta":{"name":"mapped","flags":524288},"id":86}
> mapped --- {"kind":"object","properties":[],"indexInfos":[{"keyType":{"kind":"primitive","primitive":"string","id":15},"type":{"kind":"primitive","primitive":"number","id":16},"parameterSymbol":{"name":"index","flags":1}}],"symbolMeta":{"name":"mapped","flags":524288},"id":86}
> { [index: string]: number }
> [index: string]: number
> [index: string]: number
Expand Down
9 changes: 9 additions & 0 deletions tests/baselines/reference/mappedParam.merged.types
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
=== mappedParam.ts ===

type mappedType2 = { [P in 'a'|'b'|'c']: P}
> mappedType2 --- { a: "a"; b: "b"; c: "c"; }
> { [P in 'a'|'b'|'c']: P}
> P in 'a'|'b'|'c'
> P --- P
> P
> P --- P
9 changes: 9 additions & 0 deletions tests/baselines/reference/mappedParam.tree
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
=== mappedParam.ts ===

type mappedType2 = { [P in 'a'|'b'|'c']: P}
> mappedType2 --- {"kind":"object","properties":[{"kind":"string_literal","value":"a","symbolMeta":{"name":"a","flags":33554436},"id":88},{"kind":"string_literal","value":"b","symbolMeta":{"name":"b","flags":33554436},"id":90},{"kind":"string_literal","value":"c","symbolMeta":{"name":"c","flags":33554436},"id":92}],"indexInfos":[{"keyType":{"kind":"union","types":[{"kind":"reference","id":88},{"kind":"reference","id":90},{"kind":"reference","id":92}],"id":94},"type":{"kind":"type_parameter","symbolMeta":{"name":"P","flags":262144},"id":87},"parameterSymbol":{"name":"P","flags":262144}}],"symbolMeta":{"name":"mappedType2","flags":524288},"id":86}
> { [P in 'a'|'b'|'c']: P}
> P in 'a'|'b'|'c'
> P --- {"kind":"type_parameter","symbolMeta":{"name":"P","flags":262144},"id":87}
> P
> P --- {"kind":"type_parameter","symbolMeta":{"name":"P","flags":262144},"id":87}
2 changes: 1 addition & 1 deletion tests/baselines/reference/partial.tree
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
type partialUnion = Partial<{a: string}|{b: string}>
> partialUnion --- {"kind":"union","types":[{"kind":"object","properties":[{"kind":"primitive","primitive":"string","symbolMeta":{"name":"a","flags":50331652},"id":15}],"indexInfos":[],"symbolMeta":{"name":"__type","flags":2048,"anonymous":true},"id":93},{"kind":"object","properties":[{"kind":"reference","symbolMeta":{"name":"b","flags":50331652},"id":15}],"indexInfos":[],"symbolMeta":{"name":"__type","flags":2048,"anonymous":true},"id":95}],"symbolMeta":{"name":"partialUnion","flags":524288},"id":97}
> Partial<{a: string}|{b: string}>
> Partial --- {"kind":"object","properties":[],"indexInfos":[{"keyType":{"kind":"index","indexOf":{"kind":"type_parameter","symbolMeta":{"name":"T","flags":262144},"id":87},"id":89},"type":{"kind":"indexed_access","indexType":{"kind":"type_parameter","symbolMeta":{"name":"P","flags":262144},"id":88},"objectType":{"kind":"reference","symbolMeta":{"name":"T","flags":262144},"id":87},"id":99}}],"symbolMeta":{"name":"Partial","flags":524288},"id":86}
> Partial --- {"kind":"object","properties":[],"indexInfos":[{"keyType":{"kind":"index","indexOf":{"kind":"type_parameter","symbolMeta":{"name":"T","flags":262144},"id":87},"id":89},"type":{"kind":"indexed_access","indexType":{"kind":"type_parameter","symbolMeta":{"name":"P","flags":262144},"id":88},"objectType":{"kind":"reference","symbolMeta":{"name":"T","flags":262144},"id":87},"id":99},"parameterSymbol":{"name":"P","flags":262144}}],"symbolMeta":{"name":"Partial","flags":524288},"id":86}
> {a: string}|{b: string}
> {a: string}|{b: string}
> {a: string}|{b: string}
Expand Down
2 changes: 1 addition & 1 deletion tests/baselines/reference/pick.tree
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ type p = Pick<t, "a"|"d"> & {b: "asd", d: { b: "c" }}
> Pick<t, "a"|"d"> & {b: "asd", d: { b: "c" }}
> Pick<t, "a"|"d"> & {b: "asd", d: { b: "c" }}
> Pick<t, "a"|"d">
> Pick --- {"kind":"object","properties":[],"indexInfos":[{"keyType":{"kind":"type_parameter","symbolMeta":{"name":"K","flags":262144},"id":94},"type":{"kind":"indexed_access","indexType":{"kind":"type_parameter","symbolMeta":{"name":"P","flags":262144},"id":95},"objectType":{"kind":"type_parameter","symbolMeta":{"name":"T","flags":262144},"id":93},"id":104}}],"symbolMeta":{"name":"Pick","flags":524288},"id":92}
> Pick --- {"kind":"object","properties":[],"indexInfos":[{"keyType":{"kind":"type_parameter","symbolMeta":{"name":"K","flags":262144},"id":94},"type":{"kind":"indexed_access","indexType":{"kind":"type_parameter","symbolMeta":{"name":"P","flags":262144},"id":95},"objectType":{"kind":"type_parameter","symbolMeta":{"name":"T","flags":262144},"id":93},"id":104},"parameterSymbol":{"name":"P","flags":262144}}],"symbolMeta":{"name":"Pick","flags":524288},"id":92}
> t, "a"|"d"
> t
> t --- {"kind":"object","properties":[{"kind":"string_literal","value":"b","symbolMeta":{"name":"a","flags":4},"id":87},{"kind":"string_literal","value":"d","symbolMeta":{"name":"c","flags":4},"id":89},{"kind":"object","properties":[{"kind":"reference","symbolMeta":{"name":"a","flags":4},"id":87}],"indexInfos":[],"symbolMeta":{"name":"d","flags":4},"id":91}],"indexInfos":[],"symbolMeta":{"name":"t","flags":524288},"id":86}
Expand Down
1 change: 1 addition & 0 deletions tests/cases/mappedParam.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
type mappedType2 = { [P in 'a'|'b'|'c']: P}

0 comments on commit 56942d9

Please sign in to comment.