Merge pull request #17870 from Microsoft/fix-getConstraintOfIndexedAccess
Fix getConstraintOfIndexedAccess
This commit is contained in:
commit
439cdca52f
9 changed files with 334 additions and 8 deletions
|
@ -5911,7 +5911,13 @@ namespace ts {
|
||||||
return transformed;
|
return transformed;
|
||||||
}
|
}
|
||||||
const baseObjectType = getBaseConstraintOfType(type.objectType);
|
const baseObjectType = getBaseConstraintOfType(type.objectType);
|
||||||
const baseIndexType = getBaseConstraintOfType(type.indexType);
|
const keepTypeParameterForMappedType = baseObjectType && getObjectFlags(baseObjectType) & ObjectFlags.Mapped && type.indexType.flags & TypeFlags.TypeParameter;
|
||||||
|
const baseIndexType = !keepTypeParameterForMappedType && getBaseConstraintOfType(type.indexType);
|
||||||
|
if (baseObjectType && baseIndexType === stringType && !getIndexInfoOfType(baseObjectType, IndexKind.String)) {
|
||||||
|
// getIndexedAccessType returns `any` for X[string] where X doesn't have an index signature.
|
||||||
|
// instead, return undefined so that the indexed access check fails
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
return baseObjectType || baseIndexType ? getIndexedAccessType(baseObjectType || type.objectType, baseIndexType || type.indexType) : undefined;
|
return baseObjectType || baseIndexType ? getIndexedAccessType(baseObjectType || type.objectType, baseIndexType || type.indexType) : undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5961,8 +5967,9 @@ namespace ts {
|
||||||
function computeBaseConstraint(t: Type): Type {
|
function computeBaseConstraint(t: Type): Type {
|
||||||
if (t.flags & TypeFlags.TypeParameter) {
|
if (t.flags & TypeFlags.TypeParameter) {
|
||||||
const constraint = getConstraintFromTypeParameter(<TypeParameter>t);
|
const constraint = getConstraintFromTypeParameter(<TypeParameter>t);
|
||||||
return (<TypeParameter>t).isThisType ? constraint :
|
return (t as TypeParameter).isThisType || !constraint ?
|
||||||
constraint ? getBaseConstraint(constraint) : undefined;
|
constraint :
|
||||||
|
getBaseConstraint(constraint);
|
||||||
}
|
}
|
||||||
if (t.flags & TypeFlags.UnionOrIntersection) {
|
if (t.flags & TypeFlags.UnionOrIntersection) {
|
||||||
const types = (<UnionOrIntersectionType>t).types;
|
const types = (<UnionOrIntersectionType>t).types;
|
||||||
|
@ -5990,9 +5997,6 @@ namespace ts {
|
||||||
const baseIndexedAccess = baseObjectType && baseIndexType ? getIndexedAccessType(baseObjectType, baseIndexType) : undefined;
|
const baseIndexedAccess = baseObjectType && baseIndexType ? getIndexedAccessType(baseObjectType, baseIndexType) : undefined;
|
||||||
return baseIndexedAccess && baseIndexedAccess !== unknownType ? getBaseConstraint(baseIndexedAccess) : undefined;
|
return baseIndexedAccess && baseIndexedAccess !== unknownType ? getBaseConstraint(baseIndexedAccess) : undefined;
|
||||||
}
|
}
|
||||||
if (isGenericMappedType(t)) {
|
|
||||||
return emptyObjectType;
|
|
||||||
}
|
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9289,7 +9293,7 @@ namespace ts {
|
||||||
}
|
}
|
||||||
else if (target.flags & TypeFlags.IndexedAccess) {
|
else if (target.flags & TypeFlags.IndexedAccess) {
|
||||||
// A type S is related to a type T[K] if S is related to A[K], where K is string-like and
|
// A type S is related to a type T[K] if S is related to A[K], where K is string-like and
|
||||||
// A is the apparent type of S.
|
// A is the apparent type of T.
|
||||||
const constraint = getConstraintOfType(<IndexedAccessType>target);
|
const constraint = getConstraintOfType(<IndexedAccessType>target);
|
||||||
if (constraint) {
|
if (constraint) {
|
||||||
if (result = isRelatedTo(source, constraint, reportErrors)) {
|
if (result = isRelatedTo(source, constraint, reportErrors)) {
|
||||||
|
|
|
@ -718,7 +718,7 @@ namespace ts {
|
||||||
export function sum<T extends Record<K, number>, K extends string>(array: T[], prop: K): number {
|
export function sum<T extends Record<K, number>, K extends string>(array: T[], prop: K): number {
|
||||||
let result = 0;
|
let result = 0;
|
||||||
for (const v of array) {
|
for (const v of array) {
|
||||||
// Note: we need the following type assertion because of GH #17069
|
// TODO: Remove the following type assertion once the fix for #17069 is merged
|
||||||
result += v[prop] as number;
|
result += v[prop] as number;
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
//// [additionOperatorWithConstrainedTypeParameter.ts]
|
||||||
|
// test for #17069
|
||||||
|
function sum<T extends Record<K, number>, K extends string>(n: number, v: T, k: K) {
|
||||||
|
n = n + v[k];
|
||||||
|
n += v[k]; // += should work the same way
|
||||||
|
}
|
||||||
|
function realSum<T extends Record<K, number>, K extends string>(n: number, vs: T[], k: K) {
|
||||||
|
for (const v of vs) {
|
||||||
|
n = n + v[k];
|
||||||
|
n += v[k];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//// [additionOperatorWithConstrainedTypeParameter.js]
|
||||||
|
// test for #17069
|
||||||
|
function sum(n, v, k) {
|
||||||
|
n = n + v[k];
|
||||||
|
n += v[k]; // += should work the same way
|
||||||
|
}
|
||||||
|
function realSum(n, vs, k) {
|
||||||
|
for (var _i = 0, vs_1 = vs; _i < vs_1.length; _i++) {
|
||||||
|
var v = vs_1[_i];
|
||||||
|
n = n + v[k];
|
||||||
|
n += v[k];
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
=== tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithConstrainedTypeParameter.ts ===
|
||||||
|
// test for #17069
|
||||||
|
function sum<T extends Record<K, number>, K extends string>(n: number, v: T, k: K) {
|
||||||
|
>sum : Symbol(sum, Decl(additionOperatorWithConstrainedTypeParameter.ts, 0, 0))
|
||||||
|
>T : Symbol(T, Decl(additionOperatorWithConstrainedTypeParameter.ts, 1, 13))
|
||||||
|
>Record : Symbol(Record, Decl(lib.d.ts, --, --))
|
||||||
|
>K : Symbol(K, Decl(additionOperatorWithConstrainedTypeParameter.ts, 1, 41))
|
||||||
|
>K : Symbol(K, Decl(additionOperatorWithConstrainedTypeParameter.ts, 1, 41))
|
||||||
|
>n : Symbol(n, Decl(additionOperatorWithConstrainedTypeParameter.ts, 1, 60))
|
||||||
|
>v : Symbol(v, Decl(additionOperatorWithConstrainedTypeParameter.ts, 1, 70))
|
||||||
|
>T : Symbol(T, Decl(additionOperatorWithConstrainedTypeParameter.ts, 1, 13))
|
||||||
|
>k : Symbol(k, Decl(additionOperatorWithConstrainedTypeParameter.ts, 1, 76))
|
||||||
|
>K : Symbol(K, Decl(additionOperatorWithConstrainedTypeParameter.ts, 1, 41))
|
||||||
|
|
||||||
|
n = n + v[k];
|
||||||
|
>n : Symbol(n, Decl(additionOperatorWithConstrainedTypeParameter.ts, 1, 60))
|
||||||
|
>n : Symbol(n, Decl(additionOperatorWithConstrainedTypeParameter.ts, 1, 60))
|
||||||
|
>v : Symbol(v, Decl(additionOperatorWithConstrainedTypeParameter.ts, 1, 70))
|
||||||
|
>k : Symbol(k, Decl(additionOperatorWithConstrainedTypeParameter.ts, 1, 76))
|
||||||
|
|
||||||
|
n += v[k]; // += should work the same way
|
||||||
|
>n : Symbol(n, Decl(additionOperatorWithConstrainedTypeParameter.ts, 1, 60))
|
||||||
|
>v : Symbol(v, Decl(additionOperatorWithConstrainedTypeParameter.ts, 1, 70))
|
||||||
|
>k : Symbol(k, Decl(additionOperatorWithConstrainedTypeParameter.ts, 1, 76))
|
||||||
|
}
|
||||||
|
function realSum<T extends Record<K, number>, K extends string>(n: number, vs: T[], k: K) {
|
||||||
|
>realSum : Symbol(realSum, Decl(additionOperatorWithConstrainedTypeParameter.ts, 4, 1))
|
||||||
|
>T : Symbol(T, Decl(additionOperatorWithConstrainedTypeParameter.ts, 5, 17))
|
||||||
|
>Record : Symbol(Record, Decl(lib.d.ts, --, --))
|
||||||
|
>K : Symbol(K, Decl(additionOperatorWithConstrainedTypeParameter.ts, 5, 45))
|
||||||
|
>K : Symbol(K, Decl(additionOperatorWithConstrainedTypeParameter.ts, 5, 45))
|
||||||
|
>n : Symbol(n, Decl(additionOperatorWithConstrainedTypeParameter.ts, 5, 64))
|
||||||
|
>vs : Symbol(vs, Decl(additionOperatorWithConstrainedTypeParameter.ts, 5, 74))
|
||||||
|
>T : Symbol(T, Decl(additionOperatorWithConstrainedTypeParameter.ts, 5, 17))
|
||||||
|
>k : Symbol(k, Decl(additionOperatorWithConstrainedTypeParameter.ts, 5, 83))
|
||||||
|
>K : Symbol(K, Decl(additionOperatorWithConstrainedTypeParameter.ts, 5, 45))
|
||||||
|
|
||||||
|
for (const v of vs) {
|
||||||
|
>v : Symbol(v, Decl(additionOperatorWithConstrainedTypeParameter.ts, 6, 14))
|
||||||
|
>vs : Symbol(vs, Decl(additionOperatorWithConstrainedTypeParameter.ts, 5, 74))
|
||||||
|
|
||||||
|
n = n + v[k];
|
||||||
|
>n : Symbol(n, Decl(additionOperatorWithConstrainedTypeParameter.ts, 5, 64))
|
||||||
|
>n : Symbol(n, Decl(additionOperatorWithConstrainedTypeParameter.ts, 5, 64))
|
||||||
|
>v : Symbol(v, Decl(additionOperatorWithConstrainedTypeParameter.ts, 6, 14))
|
||||||
|
>k : Symbol(k, Decl(additionOperatorWithConstrainedTypeParameter.ts, 5, 83))
|
||||||
|
|
||||||
|
n += v[k];
|
||||||
|
>n : Symbol(n, Decl(additionOperatorWithConstrainedTypeParameter.ts, 5, 64))
|
||||||
|
>v : Symbol(v, Decl(additionOperatorWithConstrainedTypeParameter.ts, 6, 14))
|
||||||
|
>k : Symbol(k, Decl(additionOperatorWithConstrainedTypeParameter.ts, 5, 83))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,64 @@
|
||||||
|
=== tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithConstrainedTypeParameter.ts ===
|
||||||
|
// test for #17069
|
||||||
|
function sum<T extends Record<K, number>, K extends string>(n: number, v: T, k: K) {
|
||||||
|
>sum : <T extends Record<K, number>, K extends string>(n: number, v: T, k: K) => void
|
||||||
|
>T : T
|
||||||
|
>Record : Record<K, T>
|
||||||
|
>K : K
|
||||||
|
>K : K
|
||||||
|
>n : number
|
||||||
|
>v : T
|
||||||
|
>T : T
|
||||||
|
>k : K
|
||||||
|
>K : K
|
||||||
|
|
||||||
|
n = n + v[k];
|
||||||
|
>n = n + v[k] : number
|
||||||
|
>n : number
|
||||||
|
>n + v[k] : number
|
||||||
|
>n : number
|
||||||
|
>v[k] : T[K]
|
||||||
|
>v : T
|
||||||
|
>k : K
|
||||||
|
|
||||||
|
n += v[k]; // += should work the same way
|
||||||
|
>n += v[k] : number
|
||||||
|
>n : number
|
||||||
|
>v[k] : T[K]
|
||||||
|
>v : T
|
||||||
|
>k : K
|
||||||
|
}
|
||||||
|
function realSum<T extends Record<K, number>, K extends string>(n: number, vs: T[], k: K) {
|
||||||
|
>realSum : <T extends Record<K, number>, K extends string>(n: number, vs: T[], k: K) => void
|
||||||
|
>T : T
|
||||||
|
>Record : Record<K, T>
|
||||||
|
>K : K
|
||||||
|
>K : K
|
||||||
|
>n : number
|
||||||
|
>vs : T[]
|
||||||
|
>T : T
|
||||||
|
>k : K
|
||||||
|
>K : K
|
||||||
|
|
||||||
|
for (const v of vs) {
|
||||||
|
>v : T
|
||||||
|
>vs : T[]
|
||||||
|
|
||||||
|
n = n + v[k];
|
||||||
|
>n = n + v[k] : number
|
||||||
|
>n : number
|
||||||
|
>n + v[k] : number
|
||||||
|
>n : number
|
||||||
|
>v[k] : T[K]
|
||||||
|
>v : T
|
||||||
|
>k : K
|
||||||
|
|
||||||
|
n += v[k];
|
||||||
|
>n += v[k] : number
|
||||||
|
>n : number
|
||||||
|
>v[k] : T[K]
|
||||||
|
>v : T
|
||||||
|
>k : K
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,67 @@
|
||||||
|
tests/cases/conformance/types/nonPrimitive/nonPrimitiveConstraintOfIndexAccessType.ts(3,5): error TS2322: Type 'string' is not assignable to type 'T[P]'.
|
||||||
|
tests/cases/conformance/types/nonPrimitive/nonPrimitiveConstraintOfIndexAccessType.ts(6,5): error TS2322: Type 'string' is not assignable to type 'T[P]'.
|
||||||
|
tests/cases/conformance/types/nonPrimitive/nonPrimitiveConstraintOfIndexAccessType.ts(9,5): error TS2322: Type 'string' is not assignable to type 'T[P]'.
|
||||||
|
tests/cases/conformance/types/nonPrimitive/nonPrimitiveConstraintOfIndexAccessType.ts(12,5): error TS2322: Type 'string' is not assignable to type 'T[P]'.
|
||||||
|
tests/cases/conformance/types/nonPrimitive/nonPrimitiveConstraintOfIndexAccessType.ts(15,5): error TS2322: Type 'string' is not assignable to type 'T[P]'.
|
||||||
|
tests/cases/conformance/types/nonPrimitive/nonPrimitiveConstraintOfIndexAccessType.ts(18,5): error TS2322: Type 'string' is not assignable to type 'T[P]'.
|
||||||
|
tests/cases/conformance/types/nonPrimitive/nonPrimitiveConstraintOfIndexAccessType.ts(21,5): error TS2322: Type 'string' is not assignable to type 'T[P]'.
|
||||||
|
tests/cases/conformance/types/nonPrimitive/nonPrimitiveConstraintOfIndexAccessType.ts(24,5): error TS2322: Type 'string' is not assignable to type 'T[P]'.
|
||||||
|
tests/cases/conformance/types/nonPrimitive/nonPrimitiveConstraintOfIndexAccessType.ts(27,5): error TS2322: Type 'string' is not assignable to type 'T[P]'.
|
||||||
|
tests/cases/conformance/types/nonPrimitive/nonPrimitiveConstraintOfIndexAccessType.ts(30,5): error TS2322: Type 'string' is not assignable to type 'T[P]'.
|
||||||
|
Type 'string' is not assignable to type 'number'.
|
||||||
|
|
||||||
|
|
||||||
|
==== tests/cases/conformance/types/nonPrimitive/nonPrimitiveConstraintOfIndexAccessType.ts (10 errors) ====
|
||||||
|
// test for #15371
|
||||||
|
function f<T extends object, P extends keyof T>(s: string, tp: T[P]): void {
|
||||||
|
tp = s;
|
||||||
|
~~
|
||||||
|
!!! error TS2322: Type 'string' is not assignable to type 'T[P]'.
|
||||||
|
}
|
||||||
|
function g<T extends null, P extends keyof T>(s: string, tp: T[P]): void {
|
||||||
|
tp = s;
|
||||||
|
~~
|
||||||
|
!!! error TS2322: Type 'string' is not assignable to type 'T[P]'.
|
||||||
|
}
|
||||||
|
function h<T extends undefined, P extends keyof T>(s: string, tp: T[P]): void {
|
||||||
|
tp = s;
|
||||||
|
~~
|
||||||
|
!!! error TS2322: Type 'string' is not assignable to type 'T[P]'.
|
||||||
|
}
|
||||||
|
function i<T extends void, P extends keyof T>(s: string, tp: T[P]): void {
|
||||||
|
tp = s;
|
||||||
|
~~
|
||||||
|
!!! error TS2322: Type 'string' is not assignable to type 'T[P]'.
|
||||||
|
}
|
||||||
|
function j<T extends never, P extends keyof T>(s: string, tp: T[P]): void {
|
||||||
|
tp = s;
|
||||||
|
~~
|
||||||
|
!!! error TS2322: Type 'string' is not assignable to type 'T[P]'.
|
||||||
|
}
|
||||||
|
function k<T extends number, P extends keyof T>(s: string, tp: T[P]): void {
|
||||||
|
tp = s;
|
||||||
|
~~
|
||||||
|
!!! error TS2322: Type 'string' is not assignable to type 'T[P]'.
|
||||||
|
}
|
||||||
|
function o<T extends string, P extends keyof T>(s: string, tp: T[P]): void {
|
||||||
|
tp = s;
|
||||||
|
~~
|
||||||
|
!!! error TS2322: Type 'string' is not assignable to type 'T[P]'.
|
||||||
|
}
|
||||||
|
function l<T extends {}, P extends keyof T>(s: string, tp: T[P]): void {
|
||||||
|
tp = s;
|
||||||
|
~~
|
||||||
|
!!! error TS2322: Type 'string' is not assignable to type 'T[P]'.
|
||||||
|
}
|
||||||
|
function m<T extends { a: number }, P extends keyof T>(s: string, tp: T[P]): void {
|
||||||
|
tp = s;
|
||||||
|
~~
|
||||||
|
!!! error TS2322: Type 'string' is not assignable to type 'T[P]'.
|
||||||
|
}
|
||||||
|
function n<T extends { [s: string]: number }, P extends keyof T>(s: string, tp: T[P]): void {
|
||||||
|
tp = s;
|
||||||
|
~~
|
||||||
|
!!! error TS2322: Type 'string' is not assignable to type 'T[P]'.
|
||||||
|
!!! error TS2322: Type 'string' is not assignable to type 'number'.
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,67 @@
|
||||||
|
//// [nonPrimitiveConstraintOfIndexAccessType.ts]
|
||||||
|
// test for #15371
|
||||||
|
function f<T extends object, P extends keyof T>(s: string, tp: T[P]): void {
|
||||||
|
tp = s;
|
||||||
|
}
|
||||||
|
function g<T extends null, P extends keyof T>(s: string, tp: T[P]): void {
|
||||||
|
tp = s;
|
||||||
|
}
|
||||||
|
function h<T extends undefined, P extends keyof T>(s: string, tp: T[P]): void {
|
||||||
|
tp = s;
|
||||||
|
}
|
||||||
|
function i<T extends void, P extends keyof T>(s: string, tp: T[P]): void {
|
||||||
|
tp = s;
|
||||||
|
}
|
||||||
|
function j<T extends never, P extends keyof T>(s: string, tp: T[P]): void {
|
||||||
|
tp = s;
|
||||||
|
}
|
||||||
|
function k<T extends number, P extends keyof T>(s: string, tp: T[P]): void {
|
||||||
|
tp = s;
|
||||||
|
}
|
||||||
|
function o<T extends string, P extends keyof T>(s: string, tp: T[P]): void {
|
||||||
|
tp = s;
|
||||||
|
}
|
||||||
|
function l<T extends {}, P extends keyof T>(s: string, tp: T[P]): void {
|
||||||
|
tp = s;
|
||||||
|
}
|
||||||
|
function m<T extends { a: number }, P extends keyof T>(s: string, tp: T[P]): void {
|
||||||
|
tp = s;
|
||||||
|
}
|
||||||
|
function n<T extends { [s: string]: number }, P extends keyof T>(s: string, tp: T[P]): void {
|
||||||
|
tp = s;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//// [nonPrimitiveConstraintOfIndexAccessType.js]
|
||||||
|
"use strict";
|
||||||
|
// test for #15371
|
||||||
|
function f(s, tp) {
|
||||||
|
tp = s;
|
||||||
|
}
|
||||||
|
function g(s, tp) {
|
||||||
|
tp = s;
|
||||||
|
}
|
||||||
|
function h(s, tp) {
|
||||||
|
tp = s;
|
||||||
|
}
|
||||||
|
function i(s, tp) {
|
||||||
|
tp = s;
|
||||||
|
}
|
||||||
|
function j(s, tp) {
|
||||||
|
tp = s;
|
||||||
|
}
|
||||||
|
function k(s, tp) {
|
||||||
|
tp = s;
|
||||||
|
}
|
||||||
|
function o(s, tp) {
|
||||||
|
tp = s;
|
||||||
|
}
|
||||||
|
function l(s, tp) {
|
||||||
|
tp = s;
|
||||||
|
}
|
||||||
|
function m(s, tp) {
|
||||||
|
tp = s;
|
||||||
|
}
|
||||||
|
function n(s, tp) {
|
||||||
|
tp = s;
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
// test for #17069
|
||||||
|
function sum<T extends Record<K, number>, K extends string>(n: number, v: T, k: K) {
|
||||||
|
n = n + v[k];
|
||||||
|
n += v[k]; // += should work the same way
|
||||||
|
}
|
||||||
|
function realSum<T extends Record<K, number>, K extends string>(n: number, vs: T[], k: K) {
|
||||||
|
for (const v of vs) {
|
||||||
|
n = n + v[k];
|
||||||
|
n += v[k];
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
// @strict: true
|
||||||
|
// test for #15371
|
||||||
|
function f<T extends object, P extends keyof T>(s: string, tp: T[P]): void {
|
||||||
|
tp = s;
|
||||||
|
}
|
||||||
|
function g<T extends null, P extends keyof T>(s: string, tp: T[P]): void {
|
||||||
|
tp = s;
|
||||||
|
}
|
||||||
|
function h<T extends undefined, P extends keyof T>(s: string, tp: T[P]): void {
|
||||||
|
tp = s;
|
||||||
|
}
|
||||||
|
function i<T extends void, P extends keyof T>(s: string, tp: T[P]): void {
|
||||||
|
tp = s;
|
||||||
|
}
|
||||||
|
function j<T extends never, P extends keyof T>(s: string, tp: T[P]): void {
|
||||||
|
tp = s;
|
||||||
|
}
|
||||||
|
function k<T extends number, P extends keyof T>(s: string, tp: T[P]): void {
|
||||||
|
tp = s;
|
||||||
|
}
|
||||||
|
function o<T extends string, P extends keyof T>(s: string, tp: T[P]): void {
|
||||||
|
tp = s;
|
||||||
|
}
|
||||||
|
function l<T extends {}, P extends keyof T>(s: string, tp: T[P]): void {
|
||||||
|
tp = s;
|
||||||
|
}
|
||||||
|
function m<T extends { a: number }, P extends keyof T>(s: string, tp: T[P]): void {
|
||||||
|
tp = s;
|
||||||
|
}
|
||||||
|
function n<T extends { [s: string]: number }, P extends keyof T>(s: string, tp: T[P]): void {
|
||||||
|
tp = s;
|
||||||
|
}
|
Loading…
Reference in a new issue