TypeScript'te bir fonksiyonu tiplemek için parametrelerin ve dönüş değerinin tipini yazarsınız: function topla(a: number, b: number): number. Fonksiyonun kendisini bir değer olarak tarif etmek için (a: number) => void gibi bir fonksiyon tipi ifadesi, özellikleri de olan çağrılabilir nesneler için call signature, farklı argüman kalıplarıyla çağrılan fonksiyonlar için overload imzaları kullanılır. İsteğe bağlı (?) ve varsayılan değerli parametreler, this parametresi ve void dönüş tipinin özel atanabilirlik kuralı da bu konunun parçasıdır.

Bu yazıda bunları sırayla ve derlenmiş örneklerle anlatıyoruz; TypeScript 6.0'ın this kullanmayan fonksiyonlardaki çıkarım değişikliğine de değiniyoruz. Örnekler TypeScript 6.0.3 ile tsc --strict --target es2022 --moduleDetection force seçenekleriyle derlendi; hata kodları ve mesajları bu derleyicinin gerçek çıktısıdır. Fonksiyonların JavaScript tarafını JavaScript fonksiyonlar ve arrow function ve default ve rest parametreler yazılarında bulabilirsiniz.

Parametre ve dönüş tipi açıklamaları

Parametre ve dönüş tipi açıklamaları

Parametre tipi parametre adından sonra, dönüş tipi parametre listesinden sonra yazılır. Parametre tipi yazılmışsa derleyici her çağrıda argümanları denetler. Tip yazmasanız bile argüman sayısı yine kontrol edilir:

typescript
function selamla(ad: string): string {
  return "Merhaba, " + ad.toUpperCase() + "!";
}

selamla("ayşe"); // geçerli
selamla(42);
// TS2345: Argument of type 'number' is not assignable to parameter of type 'string'.

Dönüş tipini çoğu zaman yazmanız gerekmez; derleyici return ifadelerinden çıkarır. Handbook, bazı ekiplerin belgeleme, kazara değişiklikleri önleme ya da tercih nedeniyle yine de yazdığını belirtir. async fonksiyonların dönüş tipi Promise<T> biçiminde yazılır:

typescript
async function sayiGetir(): Promise<number> {
  return 26;
}

sayiGetir().then((n) => console.log(n + 1)); // 27

Bir fonksiyonun nasıl çağrılacağı bağlamdan belliyse, örneğin forEach'e verilen bir callback, parametreleri tip yazmadan da doğru tipi alır. Buna contextual typing (bağlamsal tipleme) denir. Promise tarafının ayrıntıları için async/await yazısına bakabilirsiniz.

Fonksiyon tipi ifadeleri

Bir fonksiyonu parametre olarak alırken onun tipini arrow function'a benzeyen bir sözdizimiyle yazarsınız. Bu tipe type ile ad da verebilirsiniz:

typescript
type Bicimlendirici = (deger: number) => string;

function uygula(liste: number[], fn: Bicimlendirici) {
  return liste.map(fn).join(" | ");
}

console.log(uygula([1.5, 2], (d) => d.toFixed(2))); // 1.50 | 2.00
typescript
type Yanlis = (string) => void;
// TS7051: Parameter has a name but no type. Did you mean 'arg0: string'?

Call signature ve construct signature

JavaScript'te fonksiyonların özellikleri de olabilir. Fonksiyon tipi ifadesi özellik tanımlamaya izin vermediği için bu durumda bir nesne tipinin içine call signature yazılır. Sözdizimi küçük bir farkla ayrılır: Parametre listesi ile dönüş tipi arasında => değil : kullanılır:

typescript
type AciklamaliFonksiyon = {
  aciklama: string;
  (arg: number): boolean;
};

function calistir(fn: AciklamaliFonksiyon) {
  console.log(fn.aciklama + " sonucu: " + fn(6));
}

function buyukMu(arg: number) {
  return arg > 3;
}
buyukMu.aciklama = "3'ten büyük mü";

calistir(buyukMu); // 3'ten büyük mü sonucu: true

new ile çağrılan kurucular için call signature'ın önüne new eklenir: { new (s: string): Tarih }. Handbook, Date gibi hem new ile hem de onsuz çağrılabilen nesneler için iki imzanın aynı tipte birlikte yazılabileceğini gösterir.

İsteğe bağlı ve varsayılan parametreler

İsteğe bağlı ve varsayılan parametreler

Parametre adının sonuna ? eklemek onu isteğe bağlı yapar. Tipi number yazılsa bile fonksiyonun içinde parametrenin tipi number | undefined'dır, çünkü verilmeyen argümanlar JavaScript'te undefined olur. Varsayılan değer verdiğinizde ise gövdede tip number'dır; undefined gelirse yerine varsayılan değer konur:

typescript
function yuvarla(x: number, basamak?: number) {
  return basamak === undefined ? Math.round(x) : x.toFixed(basamak);
}

function kdvEkle(tutar: number, oran = 20) {
  return tutar + (tutar * oran) / 100; // oran: number
}

console.log(yuvarla(3.14159), yuvarla(3.14159, 2)); // 3 3.14
console.log(kdvEkle(100), kdvEkle(100, 10), kdvEkle(100, undefined)); // 120 110 120

Son çağrıda olduğu gibi isteğe bağlı bir parametreye açıkça undefined vermek her zaman geçerlidir; bu, argümanı hiç vermemekle aynı anlama gelir.

Callback tiplerinde isteğe bağlı parametre tuzağı

Callback alan bir fonksiyon yazarken parametreyi index?: number diye işaretlemek, "callback'i bir ya da iki argümanla yazabilirsiniz" anlamına gelmez. Aksine, "bu callback bazen tek argümanla çağrılabilir" demektir ve kullanıcıyı gereksiz kontrollere zorlar:

typescript
function herBiri(dizi: number[], cb: (deger: number, index?: number) => void) {
  for (let i = 0; i < dizi.length; i++) cb(dizi[i], i);
}

herBiri([1, 2, 3], (d, i) => {
  console.log(i.toFixed());
});
// TS18048: 'i' is possibly 'undefined'.

Handbook'un kuralı şudur: Callback'i o argümanı vermeden çağırmayı gerçekten amaçlamıyorsanız callback tipinde isteğe bağlı parametre yazmayın. JavaScript'te fazla argümanlar zaten yok sayılır, bu yüzden daha az parametre alan bir fonksiyon her zaman daha çok parametre bekleyen bir fonksiyonun yerine geçebilir. Doğru tip (deger: number, index: number) => void olur.

Rest parametreler ve parametre destructuring

Sınırsız sayıda argüman alan rest parametrenin tipi T[], Array<T> ya da bir tuple olarak yazılır. Destructuring ile alınan parametrelerde tip, destructuring kalıbından sonra gelir:

typescript
function carp(n: number, ...sayilar: number[]) {
  return sayilar.map((x) => n * x);
}

type ABC = { a: number; b: number; c: number };
function topla({ a, b, c }: ABC) {
  return a + b + c;
}

console.log(carp(10, 1, 2, 3)); // [ 10, 20, 30 ]
console.log(topla({ a: 10, b: 3, c: 9 })); // 22

Overload: aynı fonksiyonu farklı biçimlerde çağırmak

Bazı fonksiyonlar farklı sayıda ya da tipte argümanla çağrılır. TypeScript'te bunu, gövdeden önce birden fazla overload imzası yazarak ifade edersiniz. Gövdenin üzerindeki imzaya implementation signature denir ve dışarıdan görünmez; yalnızca overload imzaları çağrılabilir:

typescript
function tarihYap(zamanDamgasi: number): Date;
function tarihYap(ay: number, gun: number, yil: number): Date;
function tarihYap(ayVeyaDamga: number, gun?: number, yil?: number): Date {
  if (gun !== undefined && yil !== undefined) {
    return new Date(yil, ayVeyaDamga, gun);
  }
  return new Date(ayVeyaDamga);
}

const d1 = tarihYap(12345678);
const d2 = tarihYap(5, 5, 5);
const d3 = tarihYap(1, 3);
// TS2575: No overload expects 2 arguments, but overloads do exist that expect either 1 or 3 arguments.

Implementation imzası iki isteğe bağlı parametreyle yazılmış olsa da fonksiyon iki argümanla çağrılamaz. Aynı nedenle tek overload imzası yazıp gövdeyi parametresiz bırakırsanız fonksiyonu argümansız çağıramazsınız; derleyici TS2554 (Expected 1 arguments, but got 0.) verir. Handbook bu yüzden gövdenin üzerinde her zaman iki ya da daha fazla imza olmasını önerir. Implementation imzası tüm overload'larla uyumlu olmak zorundadır:

typescript
function donustur(x: boolean): void;
function donustur(x: string): void;
function donustur(x: boolean) {}
// TS2394: This overload signature is not compatible with its implementation signature.

Overload'ların bir sınırı da her çağrının tek bir imzaya çözümlenmesidir. Değer "string ya da dizi" olabiliyorsa hiçbir imza onu karşılamaz:

typescript
function uzunluk(s: string): number;
function uzunluk(dizi: any[]): number;
function uzunluk(x: any) {
  return x.length;
}

uzunluk(Math.random() > 0.5 ? "merhaba" : [0]);
// TS2769: No overload matches this call.

İki overload da aynı sayıda argüman alıp aynı tipi döndürdüğü için doğru çözüm overload'u tamamen kaldırıp union parametre kullanmaktır: function uzunluk(x: any[] | string) { return x.length; }. Handbook'un kuralı: Mümkün olduğunda overload yerine union tipli parametreleri tercih edin. Union tipleri için union ve intersection yazısına bakabilirsiniz.

this parametresi

JavaScript belirtimi this adında bir parametreye izin vermez. TypeScript bu boşluğu, fonksiyonun gövdesindeki this'in tipini bildirmek için kullanır. Sahte bir ilk parametre olarak yazılır ve derlenen JavaScript'ten silinir. Callback'in hangi nesneyle çağrılacağını başka bir kodun belirlediği API'lerde yaygındır:

typescript
interface Kullanici {
  id: number;
  admin: boolean;
}

const kullanicilar: Kullanici[] = [
  { id: 1, admin: true },
  { id: 2, admin: false },
];

function suz(filtre: (this: Kullanici) => boolean): Kullanici[] {
  return kullanicilar.filter((k) => filtre.call(k));
}

const adminler = suz(function (this: Kullanici) {
  return this.admin;
});
console.log(adminler.map((k) => k.id)); // [ 1 ]

Bu davranış için callback'i function ile yazmanız gerekir; handbook arrow function'larla bunun çalışmadığını belirtir, çünkü arrow function'ların kendi this'i yoktur. this'in tipi belirlenemediğinde ise noImplicitThis (strict ailesinde) hata verir:

typescript
class Dikdortgen {
  en = 2;
  boy = 3;
  alanFonksiyonu() {
    return function () {
      return this.en * this.boy;
    };
  }
}
// TS2683: 'this' implicitly has type 'any' because it does not have a type annotation.

JavaScript'teki this kuralları için this anahtar kelimesi yazısına bakabilirsiniz.

void dönüş tipinin atanabilirlik kuralı

() => void tipindeki bir fonksiyon tipi, onu uygulayan fonksiyonun değer döndürmesini yasaklamaz. Döndürülen değer yok sayılır ve çağrının sonucu yine void tipinde kalır. Bu kural sayesinde push bir sayı döndürdüğü hâlde aşağıdaki forEach çağrısı derlenir:

typescript
type VoidFn = () => void;

const f1: VoidFn = () => true; // geçerli
const v1 = f1(); // v1: void

const kaynak = [1, 2, 3];
const hedef = [0];
kaynak.forEach((x) => hedef.push(x));
console.log(hedef); // [ 0, 1, 2, 3 ]

İstisna, dönüş tipi açıkça void yazılmış fonksiyon tanımlarıdır; bunlar değer döndüremez:

typescript
const kaydet = function (): void {
  return true;
};
// TS2322: Type 'boolean' is not assignable to type 'void'.

void ile undefined, never ve unknown arasındaki farkları any, unknown, never ve void yazısında ele alıyoruz.

strictFunctionTypes: parametrelerin doğru denetimi

Fonksiyon tiplerini birbirine atarken parametreler de karşılaştırılır. strictFunctionTypes açıkken yalnızca string kabul eden bir fonksiyon, string | number ile çağrılabilecek bir fonksiyon tipine atanamaz; aksi hâlde sayıyla çağrıldığında çökerdi:

typescript
function yaz(x: string) {
  console.log(x.toLowerCase());
}

type MetinVeyaSayi = (ns: string | number) => void;
let fn: MetinVeyaSayi = yaz;
// TS2322: Type '(x: string) => void' is not assignable to type 'MetinVeyaSayi'.

Bu ayar strict ailesindedir ve TypeScript 6.0'da strict varsayılan olarak açık olduğu için ek ayar gerekmez. tsconfig belgesine göre kontrol yalnızca fonksiyon sözdizimiyle yazılmış tiplere uygulanır; { islem(x: string | number): void } gibi metot sözdizimi bu denetimin dışında kalır.

TypeScript 6.0: this kullanmayan metotlarda daha iyi çıkarım

Tipi yazılmamış parametreleri olan fonksiyonlar "bağlama duyarlı" (context sensitive) sayılır ve generic çıkarım sırasında sonraya bırakılır. Metot sözdizimiyle yazılan fonksiyonların örtük bir this parametresi olduğu için TypeScript 5.x'te bu, nesnedeki özellik sırasına bağlı tuhaf hatalara yol açabiliyordu. TypeScript 6.0 sürüm notlarına göre gövdesinde this hiç kullanılmayan bir fonksiyon artık bağlama duyarlı sayılmıyor. Aşağıdaki kod 6.0'da hatasız derlenir; sürüm notlarına göre önceki sürümlerde tuket'teki y parametresi unknown kalıyordu:

typescript
declare function cagir<T>(nesne: {
  uret: (x: number) => T;
  tuket: (y: T) => void;
}): void;

cagir({
  tuket(y) {
    return y.toFixed();
  },
  uret(x: number) {
    return x * 2;
  },
});

Diğer 6.0 değişiklikleri için TypeScript 6.0 yenilikleri yazısına bakabilirsiniz.

Özet tablo

YapıSözdizimiNe zaman
Fonksiyon tipi ifadesi(a: string) => voidCallback ve fonksiyon parametreleri
Call signature{ aciklama: string; (a: number): boolean }Özellikleri olan çağrılabilir değerler
Construct signature{ new (s: string): Nesne }new ile çağrılan kurucular
İsteğe bağlı parametrex?: numberArgüman verilmeyebilir
Varsayılan parametreoran = 20Eksik argümana değer atamak
OverloadBirden çok imza ve tek gövdeArgüman kalıbına göre farklı dönüş tipi
this parametresifunction (this: Kullanici)this değerini dışarıdaki kodun belirlediği callback

Sık yapılan hatalar

Sık yapılan hatalar: Fonksiyon tipinde parametre adını unutmak, Callback tipinde gereksiz ?, Implementation imzasını çağrılabilir sanmak, Union yeterken overload yazmak
  • Fonksiyon tipinde parametre adını unutmak: (string) => void TS7051 verir.
  • Callback tipinde gereksiz ?: Callback'i yazan herkes parametreyi undefined olasılığına karşı kontrol etmek zorunda kalır.
  • Implementation imzasını çağrılabilir sanmak: Yalnızca overload imzaları dışarıdan görünür.
  • Union yeterken overload yazmak: Aynı dönüş tipine sahip overload'lar "ya biri ya öbürü" tipindeki değerleri kabul etmez.
  • Function tipini kullanmak: Handbook'a göre Function tipindeki değerlerin çağrıları any döndürür; () => void gibi bir tip daha güvenlidir.
  • this tipli callback'i arrow function ile yazmak: Arrow function'ın kendi this'i olmadığı için beklenen nesneye bağlanmaz.

Sık Sorulan Sorular

Dönüş tipini her zaman yazmalı mıyım?

Zorunlu değil; derleyici çıkarır. Dışa açılan API fonksiyonlarında yazmak, gövde değiştiğinde dönüş tipinin kazara değişmesini önler.

Overload mı generic mi?

Dönüş tipi argümanın tipine doğrudan bağlıysa generic çoğu zaman daha sadedir. Generic fonksiyonlar için TypeScript generics yazısına bakın.

Arrow function mı function mı?

Tip açısından ikisi de aynı fonksiyon tipini karşılar. Fark this davranışındadır: this parametresi gerekiyorsa function kullanmalısınız.

Fonksiyon tiplerini interface ile de yazabilir miyim?

Evet, call signature bir interface içinde de yazılabilir. Seçim kriterleri için interface ve type farkı yazısına bakın.

Marka adları sahiplerine aittir. Bu yazı bağımsız bir teknik rehberdir; adı geçen kuruluşlarla bayilik veya ortaklık ilişkisi bulunduğu anlamına gelmez.

İlgili içerikler: TypeScript öğrenme rehberi · any, unknown, never ve void · TypeScript generics · Union ve intersection tipleri · JavaScript fonksiyonlar · JavaScript this · TypeScript ileri seviye yazısı