tsc - Typescript :: bir nesnede, değer tipini hesaplamak için anahtar dizisini kullanın
T türünde bir şemaya göre Servise geçirilen doğru türleri çıkarsamak için aşağıdaki sözdizimini kullanan bu harika fastify eklentisini (https://github.com/Coobaha/typed-fastify) buldum.
import {Schema, Service} from '@coobaha/typed-fastify';
interface ExampleSchema extends Schema {
paths: {
'GET /test/:testId': {
request: {
params: {
testId: {
type: 'string',
}
};
};
// other properties, not relevant for question
};
};
}
const exampleService: Service<ExampleSchema> = {
'GET /test/:testId': (req, reply) => {
const testId = req.params.testId // this passes ✅
const testName = req.params.testName // this fails 💔
// rest of handler
}
};
// example is simplified. Find a full example here: https://github.com/Coobaha/typed-fastify
Bu eklenti zaten harika, ama ben onu daha da ileriye taşımak istiyorum.
Bir /test/:testId dizesi verildiğinde, o URL'nin hangi parametrelere ihtiyaç duyduğunu zaten biliyoruz. Bunu iki kez (URL'de ve params nesnesinde) belirtmek zorunda olmak, bana her zaman senkron kalmalarını garanti etmek için otomatikleştirilmeye değer bir şey gibi görünüyor.
Otomatik olarak params türünü eklemenin bir yolunu bulmayı çok isterim, bunu anahtar dizisinden (endpoint URL'si) hesaplayarak. Örneğin, /test/:testId anahtarının {testId: string} türünde bir params ile olması gerekir.
Hayal et bunu yapmayı:
import {Schema, Service} from '@coobaha/typed-fastify';
interface ExampleSchema extends Schema {
paths: {
'GET /test/:testId': {
// only other properties, no need for explicit params
};
};
}
// and still have the same type-checks on the params 🤯:
const exampleService: Service<ExampleSchema> = {
'GET /test/:testId': (req, reply) => {
const testId = req.params.testId // this passes ✅
const testName = req.params.testName // this fails 💔
// rest of handler
}
};
Benim anlayışıma göre, bu varsayılan TypeScript özellikleri ile imkansız olurdu ve muhtemelen sadece ttypescript kullanarak derleme zamanı için bir eklenti ile mümkün olur.
Alternatif derleyiciler kullanmaktan ve kendi eklentilerimi yazmaktan kaçınmanın bir yolu var mı?
Verilen her türlü destek için teşekkürler
Cevap #1
Bunu, TypeScript 4.1’de tanıtılan şablon literal türleri aracılığıyla gerçekleştirebilirsiniz, ancak birçok parametreye sahip uzun yollar için, kuyruk özyinelemeli türlerin tespiti için TypeScript 4.5’e ihtiyacınız olacak: Playground
type ExtractParams<T extends string, Acc = {}> =
T extends `${infer _}:${infer P}/${infer R}` ? ExtractParams<R, Acc & { [_ in P]: string }> :
T extends `${infer _}:${infer P}` ? Acc & { [_ in P]: string } :
Acc
type Service<Schema extends BaseSchema> = {
[K in keyof Schema['paths'] & string]: (req: Req<ExtractParams<K>>, reply: Reply) => void;
}
const exampleService: Service<ExampleSchema> = {
'GET /test/:testId': (req, reply) => {
const testId = req.params.testId // this passes ✅
const testName = req.params.testName // this fails ✅
}
};
Sihir ExtractParams türündedir. Bu, iki desen deneyerek çalışır. Öncelikle, *:(param)/(rest) desenini kontrol eder. Eğer bu eşleşirse, çıkarılan param değerini biriktiriciye ekler ve rest üzerinde özyineleme yapar. Bu, bir sorgunun ortasındaki parametreleri yakalar (örneğin, GET /test/:HERE/later). Sonrasında, sondaki parametreleri tespit etmek için *:(param) desenini kontrol eder (örneğin, GET /test/:testId). Eğer bunlardan hiçbiri eşleşmezse, işlem tamamlanmıştır ve biriktirici, çıkarılan herhangi bir parametre ile birlikte döndürülür.
