infer argument

function getProperty<Type, Key extends keyof Type>(obj: Type, key: Key) {  return obj[key];}
let x = { a: 1, b: 2, c: 3, d: 4 };
getProperty(x, "e");

기본적인 동작

“e”Key extends keyof Type 에 할당 가능한지 검사하는 로직

inferTypeArgumentsinferTypesgetInferredTypes **

Key extends keyof Type 의 타입을 구하는 코드

	//getInferredTypes
  
  const constraint = getConstraintOfTypeParameter(inference.typeParameter);
	  if (constraint) {
	      // 1
	      const instantiatedConstraint = instantiateType(constraint, context.nonFixingMapper);
	      if (!inferredType || !context.compareTypes(inferredType, getTypeWithThisArgument(instantiatedConstraint, inferredType))) {
	          // If the fallback type satisfies the constraint, we pick it. Otherwise, we pick the constraint.
	          inference.inferredType = fallbackType && context.compareTypes(fallbackType, getTypeWithThisArgument(instantiatedConstraint, fallbackType)) ? fallbackType : instantiatedConstraint;
	      }
	  }

instantiateTypeinstantiateTypeWithAliasinstantiateTypeWorkergetMappedType

을 호출하고 getMappedTypecreateInferenceContextWorker 로 만든makeNonFixingMapperForContext 를 호출한다.(1) 참고

(2) Key extends keyof TypeTypesources (3) 의 Type 이 같을 경우 targets 를 호출해서

(5) 가 호출된다.

(5)는 { a : 1 } 이라는 타입을 다시 얻어오고

instantiateTypeinstantiateTypeWithAliasinstantiateTypeWorker ⇒ **getIndexTypegetLiteralTypeFromProperties

를 호출해서 결국 a 이라는 타입을 얻어낸다.

// instantiateTypeWorker
if (flags & TypeFlags.TypeParameter) {
    const mappedType =  getMappedType(type, mapper);
    return mappedType
}

//
function getMappedType(type: Type, mapper: TypeMapper): Type {
    switch (mapper.kind) {
		    //...
        case TypeMapKind.Deferred: {
            const sources = mapper.sources;
            const targets = mapper.targets;
            for (let i = 0; i < sources.length; i++) {
                if (type === sources[i]) { //2
                    return targets[i]();  // 3
                }
            }
            return type;
        }
        //...
   }
}
//createInferenceContextWorker

createInferenceContextWorker

    function makeNonFixingMapperForContext(context: InferenceContext) {
        return makeDeferredTypeMapper(
            map(context.inferences, i => i.typeParameter), //4
            map(context.inferences, (_, i) => () => {
                return getInferredType(context, i); //5
            }),
        );
    }

function createInferenceContextWorker(inferences: InferenceInfo[], signature: Signature | undefined, flags: InferenceFlags, compareTypes: TypeComparer): InferenceContext {
    const context: InferenceContext = {
        inferences,
        signature,
        flags,
        compareTypes,
        mapper: reportUnmeasurableMapper, // initialize to a noop mapper so the context object is available, but the underlying object shape is right upon construction
        nonFixingMapper: reportUnmeasurableMapper,
    };
    context.mapper = makeFixingMapperForContext(context);
    context.nonFixingMapper = makeNonFixingMapperForContext(context);
    return context;
}